Jump to content

A very basic question

- - - - -

  • Please log in to reply
21 replies to this topic

#1
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
I've come across this program:

#include<stdio.h>
int main(){
int n, scanOK, temp, counter=0;
[B]while(((scanOK=scanf("%d", &n))||1)&&getchar()!=EOF)[/B]
if (scanOK){
for(temp=n;temp/10;temp/=10);
if(!(temp%2)){
printf("%d \n", n);
counter++;
}}
printf("%d \n", counter);
return 0;}
My question is, what are the purposes of scanf and getchar() in the bolded line. The scanf receives a string of numbers from the user's keyboard, but what is the information that getchar() gets? Is it the same one as scanOK=scanf("%d", &n) because they are in the same command line? And at what point a character is =EOF. Since the program is with indefinite input arguments, the end-of-file should be typed by the user?

Edited by Alexander, 03 June 2011 - 11:16 PM.
(bbcode formatting)


#2
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
This code may work although is not a wise way to do this.

scanf will return successful tokens parsed, in this case an entered number (as it is writing to integer n). If the user types a letter, i.e. 'a' instead of '1' then it will return 0 thus breaking the while loop. To prevent this they add || 1 so that it will continue even if they had not entered an number.

If they type a letter it would break the code inside so they use if(scanOK) to prevent it from working with the character.

As there is no ready way to break the loop, they check for EOF after each scan, which in Windows will be ^Z (or control-z) of which will break the loop and run the code below, the printf statement.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#3
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
So you say that scanf will scan the typed values, only the integers of the typed value, and define scanOK. So the || 1 means that if the entered value is not an integer, it will return 1. And after all of that, it yet checks if the previously entered value is equal to EOF or ^Z as you said. Is that right?

#4
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
It is close, scanf will return scanned tokens, for example if you use "%d" in scanf it will scan for numbers in input, if it is successful it will return 1 (1 as in 1 scanned integer.)

If you were to scan for a float ("%f") and enter an integer instead (i.e. 1) then it will return 0, meaning 0 floats were read from input.

If you were scanning for two integer ("%i %i") then you should expect scanf to return 2, as in two integers are scanned. If it returns 1, then one of them is not a character (i.e. "1 a")

0 also is synonymous to "false" which will break the while loop completely instead of continuing, so that is why they added the || 1 so if there were 0 scanned elements, then it will still run, although the inner code will only run if scanOK has 1 in it (i.e. there is an actual number to work with)

"And after all of that, it yet checks if the previously entered value is equal to EOF or ^Z as you said. Is that right?"
That is correct, if you press the enter key, or type control-z it both are not numbers (0-9), so it will fall in to the getchar() statement after scanf, since the scanf does not read anything. If the getchar() is EOF, then it will break the loop and continue to the code after it.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#5
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
I compiled/started the program, and when I entered "f6" it returned 6, when I entered "658f246" it returned "658" and "246". And by
"for example if you use "%d" in scanf it will scan for numbers in input, if it is successful it will return 1 (1 as in 1 scanned integer.)" you mean that, if it scans a complete integer number it gives scanOK the value and says the function is alright (returning 1), but if the function scans a non-integer input it breaks and continues to the || 1 , which says it will be "true" anyways? And after that, in the getchar it reads the last input as \n, ^Z

#6
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
Yes that seems correct.

It will see 'f', and skip it (scanOK is 0 so it does not run) and then scans again, it sees 6 and runs the code.

The same for your other string, it skips f and sees 246 as more input so it runs separately from 658.

Your third observation is also correct, and the getchar will consume any erroneous input (i.e. getchar() either consume newline, or invalid characters once per iteration, also checking for EOF)
abcdefEOF will see EOF on its 7th iteration (of the while loop and getchar())
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#7
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
I think I got it, but can you explain the 658f246 case more deeply? I understand it skips f, but if it is separated in two parts: before and after f, how does it work with two separate inputs? Where are these separated, and how can in read two separate inputs.

#8
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
You can see for yourself with this code (experimentation is always a good idea)

#include <stdio.h>

int main(){
  int number = 0;
  int garbage = 0;
  int scanned;
  
  //%i and %d are the same, although we are working with integers, so %i is clearer (rather than %d for decimal)
  while((scanned = scanf("%i", &number)) != EOF && (garbage = getchar()) != EOF) {
    if(scanned > 0) {
      //did enter a number, i.e. scanOK in your code
      printf("scanned %i tokens (token = %i), read non integer: ASCII %i\n", scanned, number, garbage);
    } else {
      //had not read number
      printf("scanned %i tokens, read non integer: ASCII %i\n", scanned, garbage);
    }
  }
  printf("Reached end of program through EOF\n");
  return 0;
}
Feel free to run this code and enter any input you wish, i.e. 658f246 and see how it is read (and which garbage variables there are)

I've placed both inputs in to the while loop constraints, mainly because it is run first and that EOF can be a valid integer - therefor it is wise to check for scanf's return to be EOF to break the loop otherwise getchar() will not see it.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#9
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
What I don't get is how does it separate the 658f246 in 658 and 246. Where is the cycle defined to that? Thanks for the code though, I understand the EOF and tokens now :)

#10
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
Oh I apologize,

The loop will simply run infinitely, so in my example, the code with the input 659f243 will do:

place 659f243\n in to standard input (a buffer that remains with your program, of which is stdin)

scanf takes out 659, but does not touch 'f', as you have only told it to take an integer ("%i") this is why scanf does not take a newline.

The following getchar() will take the first character in the input stream and only one, in this case, "f246" is left, so "f" is placed in to my garbage variable which is displayed on the first loop still (as an ascii integer, in this case ASCII 102

We end the loop, and start the second iteration.

f is gone so 246\n remains in the input.

the second scanf iteration now takes "246" and displays it, and the remaining getchar() will take the newline (ASCII 10) and display it as a garbage variable.

The third iteration, if you type ^Z, then it scanf will accept ^Z (EOF) as an integer, although return EOF, thus breaking the loop and then ending the program otherwise if you do not type EOF it continues and reads more input and displays it appropriately.

Therefor you can assume each line displayed in my program is another iteration of the while loop.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#11
MartinV

MartinV

    Newbie

  • Members
  • PipPip
  • 11 posts
So the 659 will be "consumed" by scanf, and the components of the input that are after 659 will go to getchar() who can read only the first character. Getchar reads the f, and than again goes back to scanf. So it's like a loop, that is not specifically defined.

#12
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
Exactly - which is why you must be careful when using while loops, to make sure you input and check in the right order as it will be run many times.

Consumption is the correct term as well for what scanf does.
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users