Thread: Can you help me understand this program?

  1. #1
    Registered User SCRIPT_KITTEH's Avatar
    Join Date
    Apr 2013
    Posts
    74

    Red face Can you help me understand this program?

    Hi folks,

    I have here a program from one of my books about C and I want to verify that I am understanding correctly how it works. Here is the program:

    Code:
    #include <stdio.h>
    int main(void)
    {
            int guess = 1;
            char response;
    
    
            printf("\nPick an integer from 1 to 100.  I will try to guess it.");
            printf("\nRespond with a y if my guess is right and with an n if it is wrong");
            printf("\n\nUh...is your number %d?\n", guess);
    
    
            while((response = getchar()) != 'y')
            {
                    if(response == 'n')
                            printf("Well, then, is it %d?\n", ++guess);
                    else
                            printf("Sorry, I understand only y or n.\n");
                    while(getchar() != '\n')
                            continue;
            }
    
    
            printf("\nI knew I could do it!\n");
    
    
            return 0;
    }
    I have two questions about this program actually. The first one is concerning ++guess. I was under the apparently false impression that ++guess & guess++ were the same thing (that being guess = guess + 1). When I first wrote this program from the book into vim, I did it like guess++. The result was:

    Code:
    Uh... is your number 1? 
    
    n // my input
    
    Well, then, is it 1?
    
    n // my input 
    
    Well, then, is it 2?
    
    etc...
    Obviously the program is not meant to ask if 1 is the number the user picked twice. When I switched it to ++guess, it fixed this problem, but I have no idea why. Can someone help me understand what's going on there?

    My second question is about that while loop. I want to make sure I understand exactly how it works, please tell me if this is correct:

    1. getchar() awaits user input
    2. (assuming y was not entered) if statement evaluates input
    3. (assuming 'n' was entered) the inner while loop evaluates 'n' (before it looks at the newline character(my pressing enter)), and since n is not a newline character, "continue" makes the program ignore input past the initial 'n', in order to prevent the program from printing 2 guesses (because n and newline are both not 'y').
    4. print statement prints guess
    5. loop starts over and awaits user input again.

    I'm fairly confident I've got that right (maybe not), but I feel a bit more lost when it comes to if the user were to just press enter and nothing else into the program. Rather than trying to explain what happens, I just took a screenshot of my terminal:

    http://i.imgur.com/xgFmCjl.png

    So my question is, when I just press enter, and the else "Sorry I only understand y or n" statement is printed, why is the input immediately after that ignored? As you can see it only seems to register the second thing I enter into the program after that statement...

  2. #2
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by SCRIPT_KITTEH View Post
    I was under the apparently false impression that ++guess & guess++ were the same thing
    (++guess) increments guess first, then evaluates it as part of the expression.
    (guess++) evaluates guess first as part of the expression, then increments guess.

    In other words, if you have say
    Code:
    guess = 5;
    a = ++guess;
    b = guess;
    
    guess = 5;
    c = guess++;
    d = guess;
    then both b and d do evaluate to 6.

    a will be 6, and c 5.


    Quote Originally Posted by SCRIPT_KITTEH View Post
    My second question is about that while loop. I want to make sure I understand exactly how it works, please tell me if this is correct:
    I can't tell what you understand based on that list.

    Why don't you write it in English instead (as opposed to a bullet-point list)?

  3. #3
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    The difference between ++guess and guess++ has to do with when the increment happens relative to the value of the pre/post-increment expression being returned, and the sequence point.
    ++guess increments the value of guess, then gives the new value as the "result"
    guess++ gives the current value of guess as the "result", then increments the value
    Code:
    int x = 42;
    int a, b;
    a = x++;  // a is assigned 42, then x is incremented to 43
    b = ++x;  // x is incremented to 44, then b is assigned the new value (44)
    The only time they behave the same is when you don't actually use the result of the expression, i.e. you don't print or assign the result anywhere. Basically, if they're on their own like
    Code:
    ++guess;  // simply increment guess, don't assign the value
    guess++;
    // or in a for loop
    for (i = 0; i < 10; i++)  // would work the same with ++i
    Google "C pre post increment" for articles giving more info

    NOTE: getchar() returns an int: getchar(3): input of char/strings - Linux man page. That is so it can return all valid character values, plus any "special" values like EOF. Thus, your response variable should be declared int to properly handle this, and your loops that call getchar() should also handle the possibility of EOF.

    As for your second question, you seem to understand it more or less correctly. I would say item 4 is really a sub-section of item 2. Item 3 is incorrect in that, the while loop doesn't evaluate the 'n', it calls getchar(), which takes the next character out of the input buffer, whatever it may be ('n', 'y', '\n' or something else).

    The inner while loop is used to flush the input buffer, i.e. "eat" any leftover characters up to and including the newline from you pressing enter (a more robust flush method would check for EOF as well as '\n'). The reason pressing enter right off the bat gives you "Sorry..." has to do with the use of two calls to getchar. When you start your program, and it hits the first getchar() on line 13, you press enter. You read that newline, removing it from the buffer. It is not a 'y', so it enters the loop. Since '\n' is also not 'n', you get the sorry message. Then, your inner while loop attempts to eat everything up to the next newline, which is your second line of input, i.e. the second line of input is thus ignored.

    As you can see, this is not a very robust way to get input from the user, since it fails when you press enter right off the bat, which is a common enough error that should be handled properly. The vast majority of the time, my preference is to read a line of input into a char array (e.g. char input[256] with fgets, strip any newline (link) then use sscanf or strtol (and related funcs) to scan the input, or to access the input directly, like input[0], or strcmp(input, "some string"), etc. This tends to be much more fault tolerant, and is easy to recover from errors.
    Last edited by anduril462; 07-22-2013 at 03:56 PM.

  4. #4
    Registered User SCRIPT_KITTEH's Avatar
    Join Date
    Apr 2013
    Posts
    74
    Quote Originally Posted by Nominal Animal View Post
    (++guess) increments guess first, then evaluates it as part of the expression.
    (guess++) evaluates guess first as part of the expression, then increments guess.

    In other words, if you have say
    Code:
    guess = 5;
    a = ++guess;
    b = guess;
    
    guess = 5;
    c = guess++;
    d = guess;
    then both b and d do evaluate to 6.

    a will be 6, and c 5.
    That makes sense... Thank you.

    Quote Originally Posted by Nominal Animal View Post
    I can't tell what you understand based on that list.

    Why don't you write it in English instead (as opposed to a bullet-point list)?
    K, I'll try:

    So first the getchar() function awaits the user to input something. If the input was not 'y', which would terminate the loop, then the if/else statement looks at the input. If the input was 'n', then the program moves on to the inner while loop, which examines the input, which was technically "n\n", not just 'n', because I pressed enter. And that inner while loop ( while(getchar() != '\n') ) looks at the 'n' before the '/n', which causes that inner loop to move onto "continue;", which makes the program ignore any input past that 'n', which in turn, assures the program only does the printf("\nWell, then, is it %d?\n", ++guess); function once, and not twice (once for 'n' input and another unwanted time for the '\n' / my pressing enter), before another iteration of the loop starts. And then after that printf() statement is printed once, the loop starts over, with getchar() awaiting input again.

  5. #5
    Registered User SCRIPT_KITTEH's Avatar
    Join Date
    Apr 2013
    Posts
    74
    Quote Originally Posted by anduril462 View Post
    NOTE: getchar() returns an int: getchar(3): input of char/strings - Linux man page. That is so it can return all valid character values, plus any "special" values like EOF. Thus, your response variable should be declared int to properly handle this, and your loops that call getchar() should also handle the possibility of EOF.

    As for your second question, you seem to understand it more or less correctly. I would say item 4 is really a sub-section of item 2. Item 3 is incorrect in that, the while loop doesn't evaluate the 'n', it calls getchar(), which takes the next character out of the input buffer, whatever it may be ('n', 'y', '\n' or something else).

    The inner while loop is used to flush the input buffer, i.e. "eat" any leftover characters up to and including the newline from you pressing enter (a more robust flush method would check for EOF as well as '\n'). The reason pressing enter right off the bat gives you "Sorry..." has to do with the use of two calls to getchar. When you start your program, and it hits the first getchar() on line 13, you press enter. You read that newline, removing it from the buffer. It is not a 'y', so it enters the loop. Since '\n' is also not 'n', you get the sorry message. Then, your inner while loop attempts to eat everything up to the next newline, which is your second line of input, i.e. the second line of input is thus ignored.

    As you can see, this is not a very robust way to get input from the user, since it fails when you press enter right off the bat, which is a common enough error that should be handled properly. The vast majority of the time, my preference is to read a line of input into a char array (e.g. char input[256] with fgets, strip any newline (link) then use sscanf or strtol (and related funcs) to scan the input, or to access the input directly, like input[0], or strcmp(input, "some string"), etc. This tends to be much more fault tolerant, and is easy to recover from errors.
    Thank you!!! It's much more clear now. It helps me learn so much when y'all experts take me through the, sequence points , one bye one.

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by SCRIPT_KITTEH View Post
    If the input was 'n', then the program moves on to the inner while loop
    No. The if clause prints one thing if the input was 'n', and another thing otherwise.
    The inner while loop is always executed.

    Quote Originally Posted by SCRIPT_KITTEH View Post
    And that inner while loop looks at the 'n' before the '/n'
    No. The 'n' was already consumed by the 'response = getchar()'.

    The inner loop simply consumes input up to and including the next newline, '\n'. The 'continue' there refers to the inner 'while' loop too, so you can just replace it with an empty body, for example
    Code:
    while (getchar() != '\n') ;
    without affecting it's operation. (Having an empty body in a loop is the same as having just 'continue'.)

    Quote Originally Posted by SCRIPT_KITTEH View Post
    which in turn, assures the program only does the printf("\nWell, then, is it %d?\n", ++guess); function once, and not twice
    No.

    On line 4, you assign 'guess=1'.

    On line 10, you print the first 'guess'.

    To avoid getting "Is your number 1" twice, you need to increase 'guess' before its value is printed again on line 16.

    ('guess' has to be increased on line 16, or the "Well, then is it" would always suggest the same value.)

    If you add 'guess++;' to say line 11, and modify line 16 to have 'guess++', then you increment the guess correctly too, between every print statement.
    You just have to remember that the first print is before the while loop.

  7. #7
    Registered User SCRIPT_KITTEH's Avatar
    Join Date
    Apr 2013
    Posts
    74
    @ Nominal, I follow you up to the last part. I wasn't talking about the 1 being printed twice/increment thing there, I meant that if you *don"t* have the while(getchar()) != '\n' inside the outer while loop, then every time you enter 'n', the program does printf("\nWell, then, is it %d\n", ++guess) in response to 'n' and then also prints the sorry message in response to the newline character, before asking for input again. Like this: http://i.imgur.com/gkFpYX7.png

    ( I originally thought/said it would do the guess printf() twice, but yeah, does that once and then the sorry printf() )

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by SCRIPT_KITTEH View Post
    I wasn't talking about the 1 being printed twice/increment thing there, I meant that if you *don"t* have the while(getchar()) != '\n' inside the outer while loop, then every time you enter 'n', the program does printf("\nWell, then, is it %d\n", ++guess) in response to 'n' and then also prints the sorry message in response to the newline character, before asking for input again. Like this: http://i.imgur.com/gkFpYX7.png
    Ah, okay; sorry.

    I thought you believed that getting guess 1 twice was because of something inside the while loop.

    My "no" was meant as a response to that, as you only get the guess 1 twice if you don't increase 'guess' between the two printf()s -- the one before the while loops, and the first time printf() is called within the while loop.

    With '++guess' in the second printf(), 'guess' gets incremented before the printf(), thus no repeated guesses.

    With 'guess++' in the second printf(), 'guess' gets incremented after the printf() -- thus there is no increment between the initial printf() before the while loop, and the first printf() within the loop -- and you get the first guess twice. But, if you also increment 'guess' before the while loop, say on line 10 or so, that fixes the situation, avoiding the repeated guess.
    Last edited by Nominal Animal; 07-23-2013 at 04:05 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help creating the program cannot understand at all
    By moe84 in forum C Programming
    Replies: 1
    Last Post: 12-01-2011, 10:47 PM
  2. Help me understand this program
    By DCICJay in forum C Programming
    Replies: 2
    Last Post: 06-06-2011, 08:52 PM
  3. program that i couldn't understand
    By elton_fan in forum C Programming
    Replies: 9
    Last Post: 03-24-2007, 03:18 PM
  4. Can someone help me understand this example program
    By Guti14 in forum C Programming
    Replies: 6
    Last Post: 09-06-2004, 12:19 PM
  5. I'm a newbie and I can't understand the errors in my program
    By iluvmyafboys in forum C++ Programming
    Replies: 19
    Last Post: 02-20-2002, 10:40 AM