Earlier we looked at arrays, but they probably seemed like a convenience more than anything particularly useful, as all we’d done was replace writing a, b, c with a[0], a[1], a[2], which is more work! By using loops we can make them much more useful.

float values[5] = { 3.1f, 4.1f, 5.9f, 2.6f, 5.3f };

int i = 1;
for(i = 0; i < 5; ++i)
{
    std::cout << values[i] << std::endl;
}

First we’ve defined a small array of different values which we would like to output to the screen. Instead of repeating code and outputting each one individually, we have used something called a for loop to iterate over every element in the array and perform the same output statement on tha element.

A for loop requires a loop index variable—conventionally called i, j, or k—which is first set to a starting value. Then so long as the expression following it (i < 5) is true, the code inside the {} will be run and at the end of that code the value of i will be modified according to the last statement (++i) in the for loop. Commonly the loop index will be an int, but there’s no reason for this to be the case.

Hence the body of this loop will be run with i = 0, then with i = 1, i = 2, i = 3, and i = 4, but not i = 5 as 5 < 5 is false. Whilst a lot of loops will involve setting the index to a starting value, checking it’s less than something and then incrementing, for loops are far more powerful than that.

// This will start at i=4 and decrement, printing the array in reverse
for(int i = 4; i >= 0; i--)
{
    std::cout << values[i] << std::endl;
}

// This has no condition and so will never terminate; it is an infinite loop
for(int i = 0; ; ++i) std::cout << "i" << std::endl;

// The other parts are optional too
std::cout << "Forever and ";
for(;;)
{
    std::cout << "ever and ";
}

Sometimes we want to stop a loop even if its condition is still true, such as if we’re searching for a certain value in an array; we want the loop to continue to the end, but there’s no point searching for longer if we’ve found the element we’re looking for. We can do this using the break statement, which we’ve already seen when dealing with switch.

int array[6] = { 1, 3, 10, 9, 4, 8 };

for(int i = 0; i < 6; ++i)
{
    // Stop when the first even element is found
    if(array[i] % 2 == 0)
    {
        std::cout << "First even element is " << array[i] << " at " << i;
        break;
    }
}

Skipping certain iterations of a loop is also useful, such as if we want to skip all even values in a loop.

for(int i = 0; i < 10; ++i)
{
    // continue jumps straight back to the beginning of the loop,
    // but still runs the ++i
    if(i % 2 == 0) continue;
    std::cout << i << std::endl;
}

Of course this is a very simple example and the keen-eyed among will probably have noticed a considerable optimisation that removes the need for the continue in this case.

for(int i = 1; i < 10; i += 2)
{
    std::cout << i << std::endl;
}

Variable Scope

Note that we’ve moved the loop index into the for loop itself. This is a handy shorthand, but it comes with a caveat to do with variable scope. Every variable in C++ has a certain scope, which is the section of code in which the variable is declared. If you declare a variable inside a code block {} then it will only be available inside that block.

In the section on Conditionals you may have tried declaring a variable inside an if statement for example, but if you try and access it outside of the if you will get a compilation error.

int a = 10;

if(a < 11)
{
    int b = 15;
}

// This line won't compile because b only exists inside the if block
std::cout << b;

When compiling with Clang I get

> $ clang++ conditionals.cpp -o conditionals                                                                                             
conditionals.cpp:13:14: error: use of undeclared identifier 'b'
std::cout << b;
             ^
1 error generated.

You can however redeclare variables in a different scope to one that they’ve already been declared in, even if the scopes are nested. Whichever variable is in the closest scope to the statement using it will be affected. What you can’t do though is declare the same variable twice within the same scope.

int a = 3;

// Inside this loop a will become 0, 2, 4, 6, 8
for(int i = 0; i < 5; ++i)
{
    int a = 2*i;
    // We can't define another i because i has already been defined
    // in the scope of the for loop
    // int i = 5;
    std::cout << "a is " << a << " i is " << i << std::endl;
}
// Outside a is back to 3

As you can see it can get a little complicated! The safest thing to do is just not reuse variable names unless they haven’t been used already in the scope.We’ll be going back to scope later when we cover functions and objects, but this is all that’s crucial for now.

while Loops

The for loop isn’t the only looping construct in C++, though it is the most powerful. Using it we could create a loop that ran over and over while a given condition was true.

int userInput = 1;

// Repeatedly get input from the user until they input a 0
for(; userInput != 0; )
{
    std::cin >> userInput;
}

This is quite cumbersome and ugly though, so C++ provides a shorthand called the while loop.

int userInput = 1;

// Does exactly the same thing as before
while(userInput != 0)
{
    std::cin >> userInput;
}

Like the for loop equivalent, a while loop will not even run once if its condition is false from the start. (Hence we initialise userInput to 1, not 0.) In some circumstances though you do want your code to run at least once; enter the do while loop.

int userInput = 0;

do
{
    std::cin >> userInput;
} while(userInput != 0); // Note the ; at the end!

As the name and syntax implies we do the code in the {} while the condition is true. Just take note that the code block is in a different scope to the condition, so whilst this may look sensible it won’t compile!

do
{
    int userInput = 0;
    std::cin >> userInput;
} while(userInput != 0);

Exercise

Improve the prime number checker from the last exercise to allow for arbitrary size integers (up to long long) instead of just those less than 10. You don’t have to accept two numbers, just one will do this time.

There are a multitude of different algorithms and optimisations to check for primality, and finding primes is an active topic of research, but a simple algorithm is just to find all the factors of a number.

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>

int main()
{
    // Retrieve input from the user
    long long p = 0;
    std::cin >> p;

    // Initially assume that the number is indeed prime, then we try and prove
    // that assumption wrong by finding factors
    bool isPrime = true;

    // Every prime is odd except 2, so we check 2 first
    if(p % 2 != 0)
    {
        /* Now we can only check for divisibility by odd numbers which doubles
           the speed of the program. Since i could increase all the way to the
           value of p which is a long long, we have to make i a long long too */
        for(long long i = 3; i < p; i += 2)
        {
            // p isn't prime if it's divisible by i
            if(p % i == 0)
            {
                // No use continuining if we find a factor of p as one
                // factor is enough to stop it from being prime
                isPrime = false;
                break;
            }
        }
    }
    else
    {
        // Can't be prime if it's divisible by 2
        isPrime = false;
    }

    if(isPrime) std::cout << "Prime\n";
    else std::cout << "Not prime\n";

    return 0;
}