We also use for loops to iterate over sequences like strings and arrays. We can run into problems when we manipulate a sequence while iterating on it.
For example, if we append elements to an array while iterating on it:
vector<string> fruits = {"banana", "orange"};
for (auto fruit : fruits) {
fruits.push_back("kivi");
}
Every time we enter this loop, we add a kivi
item to the end of the array that we are iterating through.
As a result, we never make it to the end of the array. It keeps growing forever!
This is an infinite for loop. You can imagine that as programmers, we want to make sure we never write infinite loops as they make our program run forever and completely unusable.
Assignment
Follow the Coding Tutorial and let's practice with infinite for loops!
Hint
Look at the examples above if you get stuck.
In this lesson, we will explore the concept of infinite for loops in C++. Infinite loops are loops that never terminate, causing the program to run indefinitely. This can happen when we manipulate a sequence while iterating over it, such as appending elements to an array within a loop. Understanding and avoiding infinite loops is crucial for writing efficient and functional code.
Before diving into the details, let's understand the fundamental concepts of loops and sequences. A loop is a control structure that allows us to execute a block of code multiple times. Sequences, such as arrays and strings, are collections of elements that we can iterate over using loops.
Here is a simple example of a for loop iterating over an array:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
for (auto fruit : fruits) {
cout << fruit << endl;
}
return 0;
}
In this example, the loop iterates over each element in the fruits
array and prints it to the console.
When we manipulate a sequence while iterating over it, we can unintentionally create an infinite loop. This happens because the loop's termination condition is never met. Let's revisit the problematic code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
for (auto fruit : fruits) {
fruits.push_back("kivi");
}
return 0;
}
In this code, we append "kivi" to the fruits
array during each iteration. As a result, the array keeps growing, and the loop never terminates.
Let's look at a few examples to understand how to avoid infinite loops:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
size_t size = fruits.size();
for (size_t i = 0; i < size; ++i) {
fruits.push_back("kivi");
}
return 0;
}
In this example, we store the initial size of the array and use it as the loop's termination condition. This prevents the loop from running indefinitely.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
vector<string> newFruits = {"kivi"};
for (auto fruit : fruits) {
newFruits.push_back(fruit);
}
fruits.insert(fruits.end(), newFruits.begin(), newFruits.end());
return 0;
}
In this example, we use a separate collection to store the new elements and then merge it with the original array after the loop.
Here are some common mistakes to avoid and best practices to follow:
For more advanced scenarios, consider using iterators or range-based for loops. These techniques provide more control over the iteration process and can help avoid infinite loops.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
for (auto it = fruits.begin(); it != fruits.end(); ++it) {
// Do something with *it
}
return 0;
}
Here is a well-commented code snippet demonstrating the correct use of loops without causing infinite loops:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fruits = {"banana", "orange"};
// Store the initial size of the array
size_t size = fruits.size();
// Iterate over the array using the initial size
for (size_t i = 0; i < size; ++i) {
// Append "kivi" to the array
fruits.push_back("kivi");
}
// Print the final array
for (auto fruit : fruits) {
cout << fruit << endl;
}
return 0;
}
When debugging code related to loops, consider the following tips:
Example test case:
#include <cassert>
#include <vector>
using namespace std;
void testLoop() {
vector<string> fruits = {"banana", "orange"};
size_t size = fruits.size();
for (size_t i = 0; i < size; ++i) {
fruits.push_back("kivi");
}
assert(fruits.size() == 4); // Initial size + 2 new elements
}
int main() {
testLoop();
return 0;
}
When approaching problems related to loops, consider the following strategies:
In this lesson, we explored the concept of infinite for loops in C++ and how to avoid them. We discussed the importance of having a clear termination condition and provided examples and best practices to follow. By mastering these concepts, you can write efficient and functional code that avoids common pitfalls.
For further reading and practice problems, consider the following resources: