Search

7.8 — Do while statements

Consider the case where we want to show the user a menu and ask them to make a selection -- and if the user chooses an invalid selection, to ask them again. Clearly the menu and selection should go inside a loop of some kind (so we can keep asking the user until they enter valid input), but what kind of loop should we choose?

Since a while loop evaluates the condition up front, it’s an awkward choice. We could solve the issue like this:

But this only works because our initial value of 0 for selection isn’t in the set of valid values (1, 2, 3 or 4). What if 0 was a valid choice? We’d have to pick a different initializer to represent “invalid” -- and now we’re introducing magic numbers (4.13 -- Literals) into our code.

We could instead add a new variable to track validity, like so:

While this avoids the magic number, it introduces a new variable just to ensure the loop runs once, and that adds complexity and the possibility of additional errors.

Do while statements

To help solve problems like the above, C++ offers the do-while statement:

do
    statement
while (condition);

A do while statement is a looping construct that works just like a while loop, except the statement always executes at least once. After the statement has been executed, the do-while loop checks the condition. If the condition evaluates to true, the path of execution jumps back to the top of the do-while loop and executes it again.

Here is our example above using a do-while loop instead of a while loop:

In this way, we’ve avoided both magic numbers and additional variables.

One things worth discussing in the above example is that the selection variable must be declared outside of the do block. If the selection variable were to be declared inside the do block, it would be destroyed when the do block terminates, which happens before the conditional is evaluated. But we need the variable in the while conditional -- consequently, the selection variable must be declared outside the do block (even if it wasn’t used later in the body of the function).

In practice, do-while loops aren’t commonly used. Having the condition at the bottom of the loop obscures the loop condition, which can lead to errors. Many developers recommend avoiding do-while loops altogether as a result. We’ll take a softer stance and advocate for preferring while loops over do-while when given an equal choice.

Best practice

Favor while loops over do-while when given an equal choice.


7.9 -- For statements
Index
7.7 -- Intro to loops and while statements

105 comments to 7.8 — Do while statements

  • Boris

    • Piotr

      The problem lies in ASCII codes for 'a'(97) and 'Z'(90). There is no number that is bigger than 97 and smaller than 90. You will need two closed intervals for your conditional statement ['a','z'] and ['A','Z'], which translates into [97,122] and [65,90]. If I'm correct your 'if' should look like this:

  • nav

    • nav

  • SuperNoob

    There is a typo in the second program.

    Missing semicolon in line 18.

  • brainy

    How's this?

  • Harvard's CS50 makes a big deal of Do While loops and "how great they are..." just shows how much real-world experience their professors have...

  • Abdul

    The second use of a while loop with the introduction of a validity variable functions inversely as intended. Picking a valid option re-displays the menu and picking an invalid option like -1 shows as "You selected option #-1". The following should work properly:

  • giang

    Here is the code I think should be used to ask whether user want to play again:

    But when I ran this, it just kept printing "Want to play more??? _ press y/n: " and let me input a char again and again. So instead, I tried:

    And this worked as I expected, but can anybody explain where I went wrong in the first code?? Thank u so much

    • nascardriver

      The while-loops stops if the condition is false. But your condition can never be false. `play` would have to have all values at once to make your condition be false, that's impossible.

  • james

    can someone tell me why the menu is repeating twice after the answer is showing?, it should show the menu again after the answer is calculated?

    • When you enter

      Where <enter> is you pressing the enter key, the input stream stores

      Where '\n' in a line feed. When you call `scanf("%c", &ch)`, you're asking for one character to be extracted. `ch` is set to 'x', the input stream still holds

      The next time you call `scanf("%c", &menu_option)`, that \n is still there and gets stored in `menu_option`.
      If you want the first call to remove the '\n', you can add it to the format string.

      I don't know C so I can't tell if this has any side effects, but it seems to work from my tests.

    • Bombi Barlsson

      I'm not very proficient at C++, but from what I can tell

      works as a solution. I believe ignore() was mentioned in a previous tutorial. Clear removes the error flag for cin in case there's an invalid input type, while ignore ignores any character in the next 32767 characters until the next line. It's a type of flushing mechanism, and I tend to use it for my inputs for this tutorial but I have no idea if it's proper form.

      • Luiz

        will ignore everthing until it hits EOF or a new line

        • Spero

          If you use

          make sure to

          and

          or use

    • JackTheHopper

      Put the break at the end of each case not outside, because the break you are using is causing the while loop to halt. :(

  • Benur21

    Wouldn't a normal while work as good as this one?
    Like this:

    Another question: Why when I input a letter for the selection, like "r", it will loop forever?

    • You're accessing an uninitialized variable in line 8 and 9, this causes undefined behavior.
      You want the first cycle of the loop to be unconditional, so use a do-while-loop.

      > Why when I input a letter for the selection, like "r", it will loop forever?
      'r' cannot be extracted into an `int`. Extraction fails, the stream enters a failed state and all further operations on it are ignored. This is covered later in this chapter.

  • Parsa

    Why isn't this working?
    (I enter -1 as input but it doesn't go back to the do loop.

  • Anastasia

    Hi! First I would like to thank Alex and nascardriver for the wonderful work you are doing, it's the best structured and comprehensive C++ course I've ever seen. Now, I'm a C++ beginner and I wrote a little counting coins programm summarizing the latest concepts I've learned. Could somebody review it for me, please? I have some doubts about whether I've handled certain aspects correctly (or at least at an acceptable level) in particular the exit of the selection choices, the default case of the switch (what would be the best way to end the execution there?) and whether it's acceptable to create a new struct every time the while statement executes (in the printSum() function). Thank you!

    • Hi!

      - Initialize your variables with brace initializers.
      - Line 23-30 and 40-70 should use the indexes of `Coin_type`.
      - Line 35, 36: `return {};`.
      - Line 43, 49, 55... are all the same, move them out of the switch.
      - Create a coin before the switch-statement, assign to it inside the switch, return after the switch.
      - Use single quotation marks for characters.
      - Line 93 causes undefined behavior, `sum` is uninitialized.
      - Use double literals for doubles (100.0 instead of 100).
      - Inconsistent formatting, use your editor's auto-formatting feature.

      > is there a better way to handle this?
      Yes, you'll learn about `assert` later.

      > creating a new struct with each iteration
      You're not creating a "new struct", you're creating a new instance of a struct.

      > the exit is not explicit enough?
      It's fine. You could also use a `while (true)` loop and `break` to remove the need of `exit`.

      • Anastasia

        Thank you so much for your response, nascardriver, it helps a lot, I hope I understood everything right.

        > Initialize your variables with brace initializers.
        > Line 93 causes undefined behavior, `sum` is uninitialized.
        I did initialize it to 0 with brace initialization (line 85 above). Did I do it wrong?

        > Line 23-30 and 40-70 should use the indexes of `Coin_type`.
        With an enum class the compiler doesn't allow to convert an enumerator to int and it would require to use static_cast to use the indexes. I changed Coin_type to a simple enum and it works this way.

        Hopefully this looks a bit better now:

        • > I did initialize it to 0 with brace initialization (line 85 above). Did I do it wrong?
          No, that's correct. Either I missed it or you edited it after/while I looked at your code.

          - Line 31, 63: Initialize your variables with brace initializers.

          Line 17-22 should use the enumerators.
          Line 24 should use the enumerators.
          Line 35, 38, ...: You already know the type of the coin, there's no need to repeat it

          • Anastasia

            > Either I missed it or you edit it after/while I looked at your code.
            No, you didn't miss it. I did in fact edit this line shortly after posting. I was just worrying that it is not the right way(or place) to initialize it.

            > Line 17-22 should use the enumerators.
            I wasn't sure whether I should use enumerators here, because the code is just asking for the user's input.

            I think I fixed everything this time. Won't post it, because I'm afraid I've already taken too much space.

            Thank you one more time for your help and patience.

  • Envy

    I just skimmed the comments and there were only several questions if we couldn't replace the && with || in do-while loop, which is obviously wrong. But what I thought about is why won't we use

    instead of

    If we would add a 5th option to choose from you had to add another "&& selection != 5", whereas in my example you just had to change the 4 into a 5. I know this only works here because we use consecutive numbers but it just bothered me a bit :D

    • Walter

      You're right, this would work, because we use numbers as stand-in for explicit options. Selection 2 is 'Subtraction', but it's also the number 2. It makes sense to say "2 + 1 is 3", but you can't say "Subtraction + Addition is Multiplication".

      We should be using an enum for the options here, but for brevity, this has been excluded. The following doesn't really make sense, for example:

      I believe this is the reason the conditional is written the way it is.

  • SamiraFerdi

    Alex and nascardriver, can you check this for me?

    • * Initialize your variables with uniform initialization
      * Unnecessary forward declarations. Move @main below the other functions
      * @isEvenOrOdd can be done in 1 line. Give it another try
      * You don't need to switch a bool. Use an if-statement
      * @displayEvenOrOddNumberResult can also be done in 1 line

      • SamiraFerdi

        Thank you for reply!

        what about this?

  • I'm in love with alex's tutor

    Good day! Dear Mr Alex/NAS
       Can we change nested while statements to a do..while ? I kindly & respectfully  ask you to show me how to change nested while loops that you previously showed up in section 5.5 as shown below.I have been trying my best but i cant execute what i need.

    God Bless you all!!!!

    • Alex

      • I'm in love with alex's tutor

        My hero Mr Alex!
        I very++ thank you to open my eyes to see the path of programming world. I am addicted to your tutorial really.

  • Hi Alex,

    Borrowed your menu and made a simple calculator with it:

  • Star Light

    Your website is a treasure that I believe it's must-known for every C++ learner. I always recommend it for anyone how wanna begin with C++. Really love it.

    Btw, I have an idea. It is accepting a number arguments/parameters when ran program. This one is easy. But I want to make the program auto-restart when it take a wrong parameter. Anyway, let check the code:

    I've read the previous lesson about GOTO statement. But, in the end, those args only fetch when program starts. However, I don't know the return code to make program auto-restart. Or do we have other function for this case? And I would like to avoid: (replace "cls" with my name software)

    Because people said antivirus don't like it and I have to register path to window var environment. And if user change software name then it die :p

    I hope you could gimme some advice for this.

    • nascardriver

      Hi Star Light!

      There are ways to restart you program, but there's no point in doing so programmatically, because you need the user to pass new arguments.
      If you want your program to keep running even if the user entered an erroneous command line you need to manually ask the user for input (@std::cin, @std::getline) and parse it.
      This will require the user to re-enter the entire command line whereas they'd only need to press the up arrow and correct their mistake if you let them restart the application.

      • Star Light

        Yeah, you have a point. But I really want input as an array. std::cin or std::getline could do that, but it isn't as good as taking arguments right from the start.

Leave a Comment

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