Search

7.7 — Intro to loops and while statements

Introduction to loops

And now the real fun begins -- in the next set of lessons, we’ll cover loops. Loops are control flow constructs that allow a piece of code to execute repeatedly until some condition is met. Loops add a significant amount of flexibility into your programming toolkit, allowing you to do many things that would otherwise be difficult.

For example, let’s say you wanted to print all the numbers between 1 and 10. Without loops, you might try something like this:

While that’s doable, it becomes increasingly less so as you want to print more numbers: what if you wanted to print all the numbers between 1 and 1000? That would be quite a bit of typing! But such a program is writable in this way because we know at compile time how many numbers we want to print.

Now, let’s change the parameters a bit. What if we wanted to ask the user to enter a number and then print all the numbers between 1 and the number the user entered? The number the user will enter isn’t knowable at compile-time. So how might we go about solving this?

While statements

The while statement (also called a while loop) is the simplest of the three loop types that C++ provides, and it has a definition very similar to that of an if statement:

while (condition)
    statement;

A while statement is declared using the while keyword. When a while statement is executed, the condition is evaluated. If the condition evaluates to true, the associated statement executes.

However, unlike an if statement, once the statement has finished executing, control returns to the top of the while statement and the process is repeated. This means a while statement will keep looping for as long as the condition evaluates to true.

Let’s take a look at a simple while loop that prints all the numbers from 1 to 10:

This outputs:

1 2 3 4 5 6 7 8 9 10 done!

Let’s take a closer look at what this program is doing. First, count is initialized to 1, which is the first number we’ll print. The condition count <= 10 is true, so the statement executes. In this case, our statement is a block, so all the statements in the block will execute. The first statement in the block prints 1 and a space, and the second increments count to 2. Control now returns back to the top of the while statement, and the condition is evaluated again. 2 <= 10 evaluates to true, so the code block is executed again. The loop will repeatedly execute until count is 11, at which point 11 <= 10 will evaluate to false, and the statement associated with the loop will be skipped. At this point, the loop is done.

While this program is a bit more code than typing all the numbers between 1 and 10, consider how easy it would be to modify the program to print all the numbers between 1 and 1000: all you'd need to do is change count <= 10 to count <= 1000.

While statements that evaluate to false initially

Note that if the condition initially evaluates to false, the associated statement will not execute at all. Consider the following program:

The condition 15 <= 10 evaluates to false, so the associated statement is skipped. The program continues, and the only thing printed is done!.

Infinite loops

On the other hand, if the expression always evaluates to true, the while loop will execute forever. This is called an infinite loop. Here is an example of an infinite loop:

Because count is never incremented in this program, count <= 10 will always be true. Consequently, the loop will never terminate, and the program will print "1 1 1 1 1"... forever.

Intentional infinite loops

We can declare an intentional infinite loop like this:

The only way to exit an infinite loop is through a return statement, a break statement, an exit statement, a goto statement, an exception being thrown, or the user killing the program.

Here's a silly example demonstrating this:

This program will continuously loop until the user enters n as input, at which point the if statement will evaluate to true and the associated return 0; will cause function main() to exit, terminating the program.

It is common to see this kind of loop in web server applications that run continuously and service web requests.

Best practice

Favor while(true) for intentional infinite loops.

Loop variables

Often, we want a loop to execute a certain number of times. To do this, it is common to use a loop variable, often called a counter. A loop variable is an integer that used to count how many times a loop has executed. In the examples above, the variable count is a loop variable.

Loop variables are often given simple names, such as i, j, or k. However, if you want to know where in your program a loop variable is used, and you use the search function on i, j, or k, the search function will return half your program! For this reason, some developer prefer loop variable names like iii, jjj, or kkk. Because these names are more unique, this makes searching for loop variables much easier, and helps them stand out as loop variables. An even better idea is to use "real" variable names, such as count, or a name that gives more detail about what you're counting (e.g. userCount).

Loop variables should be signed

Loop variables should almost always be signed, as unsigned integers can lead to unexpected issues. Consider the following code:

Take a look at the above example and see if you can spot the error. It's not very obvious.

It turns out, this program is an infinite loop. It starts out by printing 10 9 8 7 6 5 4 3 2 1 blastoff! as desired, but then goes off the rails, and starts counting down from 4294967295. Why? Because the loop condition count >= 0 will never be false! When count is 0, 0 >= 0 is true. Then --count is executed, and count wraps around back to 4294967295 (Assuming 32-bit integers). And since 4294967295 >= 0 is true, the program continues. Because count is unsigned, it can never be negative, and because it can never be negative, the loop won't terminate.

Best practice

Loop variables should be of type (signed) int.

Doing something every N iterations

Each time a loop executes, it is called an iteration.

Often, we want to do something every 2nd, 3rd, or 4th iteration, such as print a newline. This can easily be done by using the modulus operator on our counter:

This program produces the result:

01 02 03 04 05 06 07 08 09 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 42 43 44 45 46 47 48 49 50

Nested loops

It is also possible to nest loops inside of other loops. In the following example, the inner loop and outer loop each have their own counters. However, note that the loop expression for the inner loop makes use of the outer loop's counter as well!

This program prints:

1
1 2
1 2 3
1 2 3 4
1 2 3 4 5

Quiz time

Question #1


In the above program, why is variable inner declared inside the while block instead of immediately following the declaration of outer?

Show Solution

Question #2


Write a program that prints out the letters a through z along with their ASCII codes. Hint: to print characters as integers, you have to use a static_cast.

Show Solution

Question #3


Invert the nested loops example so it prints the following:

5 4 3 2 1
4 3 2 1
3 2 1
2 1
1

Show Solution

Question #4


Now make the numbers print like this:

        1
      2 1
    3 2 1
  4 3 2 1
5 4 3 2 1

Hint: Figure out how to make it print like this first:

X X X X 1
X X X 2 1
X X 3 2 1
X 4 3 2 1
5 4 3 2 1

Show Solution


7.8 -- Do while statements
Index
7.6 -- Goto statements

372 comments to 7.7 — Intro to loops and while statements

  • J34NP3T3R

    i got the wrong answer but still posted A to Z

        int asciiNum{ 65 };
        while (asciiNum <= 90)
        {
            std::cout << static_cast<char>(asciiNum) << '\n' ;
            ++asciiNum;
        }

  • Patrick

    For Question 2 in the quiz,

    Do myChar and 'z' get implicitly converted to integers for the comparison?

    • nascardriver

      `char` is an integral type. Character literals are nothing but fancy-looking integers.

      • Patrick

        Oh k, thx. Does the same thing go for Booleans as well? If so, would floating point numbers be the only fundamental data type that isn’t stored as an integer?

        • nascardriver

          Anything can be stored as an integer, because everything can be interpreted as an integer. That includes floating point numbers and bools. It's only a matter of how the stored values are interpreted later on. The compiler knows that something is a float or a bool, so it will generate instructions that interact with the data accordingly.

          The CPU would happily treat anything as an integer if told to.

          Let's take a little journey to assembly to show that there are no types

          All these types are different, in C++ at least.

          To understand the example, you need to know a few things about assembly

          A word is a 2 byte variable.
          A dword is a double word -> 2*2 bytes = 4 bytes
          A qword is a quad word -> 4*2 bytes = 8 bytes

          `[something]` accesses the memory at address `something`. For example

          `rip` and `rbp` are variables. Their values don't matter for this example.
          `xmm0` and `xmm1` are 16 byte variables. In this example only the first 4/8 bytes of these variables are used.

          The compiler turns the above code into

          All of the variables, no matter their type, are stored at [rbp-something]. [rbp-something] is just stack memory, there are no types at all. When reading this assembly, we know that [rbp-22] (c) for example is a 2 byte variable, because `mov word` was used to access it.
          Apart from knowing that it's a 2 byte variable, we know nothing. It could be an integer, a char, a float, we can't tell. We can see it has the value 123 so it's likely an integer, but that's just a guess.
          (d), (g) and (h) are identical, apart from having different values. To the CPU, all these values are just numbers. It could read from anywhere without worries, eg.

          This would read a part of `e`, the entirety of `d`, `c`, and `b`, and write this jam of values into `rax`. That doesn't make sense of course, these are different variables, but the CPU absolutely does not care nor know. If it's told to do something it will do so.
          The compiler simply doesn't tell the CPU to access the variables like that. The compiler knows that it stored a `std::uint8_t` at [rbp-23], so it won't generate instructions that treat [rbp-23] as anything but a `std::uint8_t` (I'm ignoring optimizations, optimizations make everything weirder).

          To our floating points `xmm0` (f) and `xmm1` (e). They're moved into the same memory as all the other variables, we've seen that. But they're coming from somewhere else. In line 1 and 2 of the assembly, they're loaded from another part of memory. Looking at that, we see

          LCPI1_0 is a quad word and LCP1_1 a long (Double word). My disassembler shows those values as hexadecimal integers, it could show them as anything, there are no types after all (My disassembler figured out that those values are floating point numbers, but it's just guessing too). Again, there are no types.

          We can guess that those values are floating point numbers, because their integer values look like floating point values to someone who's see them before, and because those values are stored in `xmm` variables. `xmm` variables can store integers just as well, so we're guessing again, but they're often used for floating point numbers.

          Enough assembly, bottles.

          How do you know you can drink from a bottle?
          It says "water" on the label, what if there is no label?
          The bottle is transparent, what if it's opaque?

          You can't know that you can drink from a bottle, until you try, and that attempt may or may not end well. Your hands and mouth are capable of drinking from a bottle that contains sand, probably.

          Still, you drink from bottles without worries, because you trust whoever filled the bottle that they've put a drinkable liquid inside. If the bottle manufacturer/filler made a mistake, you won't feel well, but you can drink paint as if it were apple juice.

          The bottle manufacturer should not make such a mistake, just like the compiler should not tell the CPU to treat memory that contains a float like a bool.

          I hope this helped more than it was confusing. If you have any questions, feel free to ask.

          You can find the above code+disassembly and play around with it at https://godbolt.org/z/cobxWM

          • Patrick

            Wow, thanks for the long explanation. I'm understanding this as there technically being no types, with the compiler giving the CPU the specific instructions on how to interpret the data.

            One more question:
            For your above example, when the compiler turned the C++ code into assembly, did all the values (including the floating points) "turn" into integers? (Meaning were they were stored as integers)
            And from there, with the compiler's instructions, the CPU can interpret those integers accordingly?

            • nascardriver

              All values are stored in binary, that's the only thing computers can do. In the disassembled output of my previous comment, the values are displayed as decimal/hexadecimal integers. Their representation doesn't matter through, as it's just a visual aid for humans.

              All the values I've shown are not integers, not floating points, not anything. They're just bits and we're looking at a pretty representation.

              Most operations operate on integers, so that's a good way to represent values of unknown type.

              • Patrick

                Ohhh ok. When you said "All the values I've shown are not integers, not floating points, not anything. They're just bits and we're looking at a pretty representation.", is this similar to how you can say a car (or bottle, or anything) is like a pretty representation of matter?

  • My initial stab at Question 4. Your solution was a lot less typing, but I got it printing correctly this way. I need more experience to think as abstractly as your solution. :)

  • Getting the hang of this! My solution was simply the inverse of what your solution was. I used the ascii number and cast it to the char.

  • Waldo Lemmer

    Thank you for another amazing lesson :)

    1. Section "While statements", the longest paragraph:
    > Control now return back to the top of the while statement, and the condition is evaluated again.
    "return" should be "returns"

    2. Question #2's answer:
    2.1 Line 8 has an extra space:

    2.2 I think `mychar` should be `myChar`

    The questions were fun, looking forward to the next lessons!

  • Ajanakoji

    Question #4

    MY solution

  • yeokaiwei

    1. As a suggestion, perhaps you would like to create a user codebase?

    Currently, we're just submitting it by WordPress in the comments.

    If you have a userbase, each user will submit their own variant on how to code each Quiz question and you will create a gigantic database where you can analyze the difference in code, where are the common mistakes, where are the common misintepretations, what is the most common coding style, etc

    For those who can't solve the Question or don't want to, just click skip.

    2. This is what I did for Quiz 4.
    Is it acceptable?

  • yeokaiwei

    How I did it. I went on Google to find int(a) and char(a) and took the ASCII code directly.

    Does it qualify for Quiz 2?

    • yeokaiwei

      This is my answer for Quiz 3.

      Is this acceptable?

  • Tobito

    currently all the comments is disappear for all pages

  • Aditya

    Here's my solution to #4, took really long lol. As always thank you for these tutorials!!

  • Uyph

    Hi, in relation to quiz 4, i want to ask why decrementing counter inside the second while loop changes the final value of it, causing the program to overflow. Don't changes made to it only exist inside the loop?

  • Artyom

    This is my solution for task 4

  • Attila

    This is question 4 just all done with while loops

  • Andreas Krug

    Small fix in second code example of chapter Iteration, line 12:
    std::cout << '0' << count << ' '; instead of std::cout << "0" << count << ' ';

  • Martin

    The section on loop variables contains the statement "Then --count is executed, and count overflows back to 4294967295."
    Earlier in the tutorial I learned that unsigned integers do not overflow, but wrap around, so I suggest to use this wording here, too. :)

  • Christian

    Hi and thank you so much for this tutorial. I am a newbie and I am confused about while loops. In the following program I am trying to calculate the standdev of a set of numbers from a file

    The number in the current file are
    80
    90
    just to test the program. What I really don't understand is why it reads the last value twice.

    Thanks for all your help.

    /**************************************************************
    Description: Design and write a C++ program that reads a set of
                 scores from the file scores.dat, and
                             outputs their
                 mean and standard deviation on cout.
    **************************************************************/

    #include <iostream>
    #include <fstream>
    #include <cmath>

    using namespace std;

    int main(){
        // Declare input stream
        ifstream inData;
        
        // Declare variables
        int count = 0;
        float value;
        float valueSquared = 0.0;
        float sum = 0.0;
        float sumSquared = 0.0;
        float average, standDev;
        // Open file
        inData.open("scores.dat");
            if(!inData){
                cout << "Cannot open the file." << endl;
                return 1;
            }
        //inData >> value;
        //valueSquared = value*value;
        //cout << value << ", " << valueSquared << endl;
        //sum = value;
        //sumSquared = valueSquared;
        //cout << sum << ", " << sumSquared << endl;
        // Get values and calculate sums
        while(!inData.eof()){ //Set the EOF flag
            inData >> value;
            valueSquared = value*value;
            cout << value << ", " << valueSquared << endl;
            sum = sum + value;
            sumSquared = sumSquared + valueSquared;
            count++;
        }
        cout << count << ", " << sum << ", " << sumSquared << ", " << sum*sum << endl;
        // Check that you have at least two values
        if(count<=1){
            cout << "Average and standard deviation are meaningless for one single value." << endl;
            return 1;
        }
        else{
            // Calculate average
            average = sum/count;
            // Calculate standard deviation
            standDev = sqrt((sumSquared-(sum*sum))/(count*(count-1)));
            // Output results
            cout << "The average and standard deviation of the set of scores are\n" <<
                "average: " << average << "\n" << "standard deviation: " << standDev << endl;
        }
        return 0;
    }

  • Tony

    For me, this is by far the most difficult chapter ever, at least the questions. Didn't seem to actually get a correct solution at all, and had to check the solutions multiple times (and I still haven't understood much). I guess I'll have to re-do this chapter later on. Thanks for the tutorials again :)

  • #Prints alphabets a - z

    • Ohh... Sorry I made a mistake in my code
      Correct one

  • Hi there, I am having some little issues understanding your code understand Nested Loops

    This code

  • for whatever reason, on debian 10, using the terminal in VSCode, my initial solution to the last problem resulted in the shape of some sort of binary tree being printed out.. just to make sure it was an issue with my code, i tried the provided solution, however the same thing resulted when compiling and executing the answer.

    ended up doing this really weird and gross looking answer that doesnt satisfy me all that much, but i assume it'll get cleaner in time

    • nascardriver

      I suppose you saw the cool pyramid

      This happens if you print a single space, rather than 2 spaces, in line 21 of the quiz's solution. It might also happen if you're not using a monospace font.

  • AE35_Unit

    Okay, got all three. Wanted to use a continue; in solution 2 but that's section 5.8. Comments and few questions in the code, thanks. Cheers.

    • nascardriver

      - You're not using `argc` or `argv`, so they shouldn't be there.
      - Your formatting will only get worse. Use an auto-formatter.
      - Use characters, not integers, for characters.
      - Avoid `static` variables, your code isn't reusable.

      • AE35_Unit

        Thanks Nascar.  Got it cleaned up and made the changes. I understand what you mean by use chars 'A' and not number 65, saw and got it fixed. I want to make sure I understand the static comment.  I move the variable to outside the while loop so I could just use int variable and not static int variable. Your comment "your code isn't reusable" is in reference to that (static int variable) not being able to be used from an outside or another file as in a header file.  I re-read chapter 6.6 and that's what I gathered from it, just wanted to make sure I understood.  Thanks for the help. Cheers.

        • nascardriver

          You wrote everything in `main`, so I had to say "code" isn't reusable rather than "function xx" isn't reusable, which would be easier to grasp. Say you wrote a function instead

          Now what happens when you call `quiz4()`? Everything is as it was before.
          What happens when you call `quiz4()` a second time? It's broken, because `printNumber` was never reset.
          If `printNumber` is non-static, it doesn't matter how often you call `quiz4()` or where you call it from. The function the same every time it called (If we ignore `std::cout`'s flags, which could be changed from the outside). The way you solved it now is correct.

          Keeping `printNumber` `static` and resetting it at the end of the function would have seemingly solved them problem, but it would still be there, just in a different case. `quiz4()` could only be called from one place at a time. That's what we do throughout the entire tutorial so you wouldn't have noticed that it's broken. But if you get into multithreading, this is no longer the case. `quiz4()` could run several times in parallel. But if all running `quiz4()`s share a variable, you'll run into trouble.

  • John

    Hello, I quite don't understand the last example. If "inner" is created and initialized after every iteration, then how does it print "1 2", "1 2 3", etc.

  • Amir

    maybe not the best, but it is how  I was thinking before looking at solution !

  • Jimmy

    This is really discouraging cause after nearly an hour of work, mainly due to sorting out bugs through trial and error, this is the best solution i can come up with. Yet the solution posted is way simpler. I am having serious doubt about me becoming a programmer.

    The worst part is if you ask me to write the same program a few hours later. I would have to trial and error my way through the same bugs.

    • nascardriver

      And every time you do it, you get better. You can't expect from yourself to be able to write great code after reading a tutorial. You'll only get better by writing code, failing, and looking up how to do it properly.

  • Joshua

    This is my honest first attempt... I was kinda reaching on this one... Is my solution with the string initialization less effective than just putting a conditional in there?

    • nascardriver

      `std::string` has to perform a dynamic memory allocation (If the string is long enough), which makes it slower than printing manually. You're not going to notice this, so your solution is fine. "\n" should be '\n'.

  • samira

    Hello Nascardriver and Alex,

    The following is the short solution to the question 3 using JUST one (do)while-loop. (I put two versions)

    SOLUTION VERSION ONE

    SOLUTION VERSION TWO

    • Petter Nybråten

      Instead of the string of spaces below, is it better to concatenate two space chars? How about three? Asking with regards to performance.

  • sami (typo)

    " In the following example, the inner loop and outer loops each have their own counters."

    Shouldn't it be "and outer loop"?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]