Missing value with scanf in C89 - c

Hope you guys can help me with a problem I'm having.
So I have this C code snippet:
int i = 0;
int q = 0;
scanf("%d %d", &i, &q);
When the user enters 4 5, the values 4 and 5 are stored in i and q respectively.
But when the user enters 99.99, 99 is stored in i, but the other 99 after the point goes missing. I do know how scanf works and I understand that scanf will stop scanning after ., but where does the second value go to?
Even if I do:
int i = 0;
int q = 0;
int k = 0;
scanf("%d %d", &i, &q);
scanf("%d", &k);
I still cant get the second value. Where does the second value go and how can I get store it in my variable?

It is trying to look for integer but it is not integer that is left in stdin so it fails.
You can get desired behavior if you do this
int a,b;
scanf("%d.%d",&a,&b);
printf("%d,%d",a,b);
This prints 99,99 as expected.
For your information,
scanf stops at the first mismatch and leaves the rest of the target objects untouched.
Also the unmatched data is left in the input buffer, available for a subsequent read using scanf etc.
Also you can check the return value to determine how many "items" scanf matched.

scanf() doesn't ignore the . while expects to read an int. I suggest you use fgets() and then try to parse with sscanf() which is a better approach. Something like:
#include <stdio.h>
int main(void)
{
int i = 0;
int q = 0;
char line[256];
if (fgets(line, sizeof line, stdin)) {
if (sscanf(line, "%d.%d", &i, &q) == 2) {
printf("i=%d, j=%d\n", i, q);
} else {
printf("Coudn't scan 2 ints\n");
}
} else {
printf("Couldn't read a line\n");
}
}
Note that fgets() will also read in the newline character if there's space in the buffer. It doesn't matter in this case.
But you need to be aware of it and may be undesirable in some cases (e.g., reading a name).
Also see: Why does everyone say not to use scanf? What should I use instead?

Let X be the following statement...
I do know how scanf works
Let Y be the following statement...
... but where does the second value go to?
X and Y do not correspond. Either you know about the return value of scanf, or you don't know how scanf works. That's the bottom line.
... but where does the second value go to?
According to the fscanf manual,
The fscanf() functions shall execute each directive of the format in turn. If a directive fails, as detailed below, the function shall return. Failures are described as input failures (due to the unavailability of input bytes) or matching failures (due to inappropriate input).
The space directive won't match the . input, which will cause a matching failure, scanf will return, meaning that nothing beyond the . byte will be consumed from stdin. In fact, scanf will even put the . back into stdin for you, just before it returns... Isn't that nice?
I gather you understand this (because you said "I do know how scanf works"), yet you seem to be asking about that second value which couldn't have been read due to a previous matching failure... thus, the answer to your first question is, the second value doesn't exist. It can't exist yet, because an input format error exists prior to it.
While we're on the topic of scanf, return values and matching failures, for the remaining question I'm going to assume your code actually looks something more like this:
int i = 0;
int q = 0;
if (scanf("%d %d", &i, &q) != 2) {
puts("Discarding invalid input");
scanf("%*[^\n]"); // discard up to the next newline and...
getchar(); // discard that newline, too.
// XXX: You might want to `return` or `continue` or something
}
... and how can I get store it in my variable?
As mentioned earlier, the space directive doesn't match the period. You need to replace that with something which manipulates the input stream as you intend... That's difficult for me to answer, because I don't have that information, but here's what I'd suggest: Split the two conversions up into two separate calls to scanf. This should give you a nice place in between the two calls to scanf to insert your period-handling code.
Here's a before & after, side-by-side, of the idiom I've adopted for that kind of input:
// BEFORE // AFTER
if (scanf("%d %d", &i, &q) != 2) { if (scanf("%d", &i) != 1) {
// ... puts("Invalid first input...");
// XXX: Handle this???
// XXX: `return` or `continue` or something
}
// XXX: Read a character, check if it's a space (or a dot, or something else which you have in your mind and I don't have in mine)
if (scanf("%d", &q) != 1) {
// ...
Handling the erroneous period could be as simple as exiting the process or calling getchar() to discard a single character, then continueing in order to try again. These solutions won't solve all user input errors, and might even confuse or frustrate some users. I recommend sticking with the discard the rest of the line method, at least until you learn about all of the alternatives for user input and what they're each best at. After all, people tend to avoid using software which confuses them...
... hence the reason people tend to discourage scanf... because I guess there's no such thing as literature which sets a standard, and thus there's no way to correct my scanf-related misunderstandings.

Related

How to fix infinite loops when user enters wrong data type in scanf()?

C beginner here. For the program below, whenever the user inputs a character or a string it enters an infinite loop. How would you fix this while still using scanf? And what would be the better methods of writing this program rather than using scanf? Thanks to those who will answer.
#include <stdio.h>
#include <ctype.h>
int main() {
int rounds = 5;
do {
printf("Preferred number of rounds per game. ENTER NUMBERS ONLY: ");
scanf("%d", &rounds);
} while(isdigit(rounds) == 0);
return 0;
}
Using 'scanf' require the input to be formatted. Scanf has very limited ability to handle bad input. The common solution will be to use fgets/sscanf, following the structure below:
char buff[256] ;
int rounds = 0 ;
while ( fgets(buff, sizeof(buff), stdin) ) {
if ( sscanf(buff, "%d", &rounds) == 1 ) {
// additional verification here
break ;
} ;
} ;
// Use rounds here ...
The fgets/sscanf will allow recovery from parsing error - the bad input line will be ignored. Depending on requirement, this might be accepted solution.
I'd say there are just two "fixes".
Retain the scanf call(s), warts and all. Carefully refrain from typing non-digits when scanf is expecting digits.
Abandon scanf and use something else. We've just been discussing this tactic over at this new question.
Once you're using scanf, it's always tempting to try to "fix" it, so there's potentially a third answer lurking here, explaining how to do better, more user-friendly, more error-tolerant input while still using scanf. In my opinion, however, this is a fool's errand, a waste of time. The easy, obvious fixes for scanf's many deficiencies are themselves imperfect and have further deficiencies. You will probably spend more time trying to fix a scanf-using program than you would have spent rewriting it to not use scanf -- and you'll get overall better results (not to mention a cleaner program) with the non-scanf-using rewrite anyway.
Change
scanf("%d", &rounds);
To
int ret;
if ((ret = scanf(" %d", &rounds)) != 1) { // Skip over white space as well
if (ret == EOF) break;
scanf("%*[^\n\r]"); // Consume the rest of the line
}
If you really like scanf, you can use getch to discard non-numeric input:
int rounds = MIN_INT;
while (scanf("%d", &rounds)) != 1)
if (getc() == EOF) /* discard a rubbish character */
break; // or other error-handling return
// rounds is only valid if we did not break, when its value should be MIN_INT.
// but you might need another indicator
C beginner here as well. Like you, I use scanf, and it can be problematic sometimes.
I've had your same problem and tried to solve it with scanf and basic stuff before finding a better solution.
I've tried different solution from here but I continue to have the same problems again and again, like if I type:
a number followed by a character (e.g. 123a), the result is a valid number (which i don't want); the result is '123'.
a string of numbers and chars that begin with a number (e.g. 1a2b3), the result is still a valid number which is '1'.
a char at the beginning (e.g. a123) can generate infinite loop.
... and so on... I've tried do...while, only while, for... nothing.
The only solution I have found to prompt the user until he/she writes only numbers is the following, but...
NOTE: if the user type a space, the program considers only the part before it, e.g. '12 3', only 12 is considered, 3 doesn't exist... unless you want use an infinite loop like I did so, in this case, you can enter multiple numbers, check them and run your program on them all at once. e.g.: '12 23 34 45' ...
NOTE 2: this is a very basic beginner solution, I am learning, and this is just what I found with what I know. Can't do any better right now and, as I said, I didn't find any other solution that I liked the output.
NOTE 3: I use the counter to sum up all the inputs that are not numbers and store the value if it finds one. If I don't use this solution I'll end up in the case where if the first character is a number but the rest aren't, it's still valid (e.g.: '12w3' is 12, which I don't want)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main (void)
{
while (1) // try also multiple inputs separated by space
{
char str[10]; // should be enough for short strings/numbers (?!)
int strlength, num, counter;
do
{
printf("Enter a number: ");
scanf("%s", str);
strlength = strlen(str);
counter = 0;
for (int i = 0; i < strlength; i++)
{
if (!isdigit(str[i]))
counter++;
}
if (counter != 0)
printf("%s is not a number.\n", str);
} while (counter != 0);
num = atoi(str);
printf("%d is a number. Well done!\n\n", num);
}
}
You can also put it in a function and away from the main().

Why doesn't scanf() wait for the next input if I previously entered a certain input

below is my simple code to enter a number and print it. it is inside a while(1) loop so i need to "Enter the number infinite number of time- each time it will print the number and again wait for the input".
#include<stdio.h>
int main()
{
int i;
while(1){
printf("\nenter i \n");
scanf("%d", &i);
if(i==1)
{
printf("%d \n", i);
}
}
return 0;
}
it was working fine. but suddenly i noticed that IF i ENTER a character(eg: "w") instead of number , from there it won't ask for input!!!**
it continuesly prints,
enter i
1
enter i
1
......
when i debug using GDB, i noticed that after i enter "w", that value of character "w" is not stored in &i . before i enter "w" it had 0x00000001 so that "1" is printed through out the process.
Why it doesn't ask for another input? According to my knowledge, when I enter "w" the ascii value of "w" should be stored in &i. But it doesn't happen.
If I put, "int i; " inside while loop it works fine! Why?
Please test my code in following way:
Copy and paste and run it
When "enter i" prompt will come enter 1
Second time enter "w". See what happens...
scanf with %d format specifier will read everything that "looks like a number", i.e. what satisfies the strictly defined format for a decimal representation of an integer: some optional whitespace followed by an optional sign followed by a sequence of digits. Once it encounters a character that cannot possibly be a part of a decimal representation, scanf stops reading and leaves the rest of the input data in the input stream untouched (to wait for the next scanf). If you enter just w, your scanf will find nothing that "looks like a number". It will read nothing. Instead it will report failure through its return value. Meanwhile your w will remain in the input stream, unread. The next time you try your scanf, exactly the same thing will happen again. And again, and again, and again... That w will sit in the input stream forever, causing each of your scanf calls to fail immediately and your loop to run forever (unless your uninitialized variable i by pure chance happens to start its life with the value of 1 in it).
Your assumption that entering w should make scanf to read ASCII code of w is completely incorrect. This sounds close to what %c format specifier would do, but this is not even close to what %d format specifier does. %d does not read arbitrary characters as ASCII codes.
Note also that every time you attempt to call that scanf with w sitting in the input stream, your scanf fails and leaves the value of i unchanged. If you declare your i inside the loop, the value of i will remain uninitialized and unpredictable after each unsuccessful scanf attempt. In that case the behavior of your program is undefined. It might even produce an illusion of "working fine" (whatever you might understand under that in this case).
You need to check the return value of scanf as well, as it will return the number of successfully scanned and parsed values. If it returns zero (or EOF) then you should exit the loop.
What happens when you enter e.g. the character 'w' instead of a number is that the scanf function will fail with the scanning and parsing, and return zero. But the input will not be removed from the input buffer (because it was not read), so in the next loop scanf will again read the non-numeric input and fail, and it will do this infinitely.
You can try this workaround:
int main()
{
int i;
char c;
while (1)
{
printf("enter i: ");
if (scanf("%d",&i) == 0)
scanf("%c",&c); // catch an erroneous input
else
printf("%d\n",i);
}
return 0;
}
BTW, when were you planning to break out of that (currently infinite) loop?
You need to read up on scanf(), since you seem to be basing your program around some assumptions which are wrong.
It won't parse the character since the conversion format specifier %d means "decimal integer".
Also, note that you must check the return value since I/O can fail. When you enter something which doesn't match the conversion specifier, scanf() fails to parse it.
You would probably be better of reading whole lines using fgets(), then using e.g. sscanf() to parse the line. It's much easier to get robust input-reading that way.
scanf return type can be checked and based on that inputs can be consumed using getchar to solve your problem.
Example code
int main()
{
int i;
int ch;
while(1){
printf("\nenter i \n");
if ( scanf("%d", &i) !=1 )
{
/*consume the non-numeric characters*/
for (; (ch = getchar()) != EOF && ch != '\n'; ) { }
}
if(i==1)
{
printf("%d \n", i);
}
}
return 0;
}
Description:
When scanf("%d", &i) encounters the character, it will not read it. The character will still remains in the input stream. So to consume those characters, getchar() can used. Then scanf will wait for the next input in further iteration.

reading string with space

Say for example I have the following main function:
int main()
{
char a[1023];
while (scanf("%s",a) != EOF)
{
printf("%s ",a);
}
}
If I input
a ab abc(newline)
it should output:
a ab abc(newline)
but the main function output
a ab abc (newline) // there is a space between newline and the last string
I want to read the string one by one. What is the problem and how can I delete that space? Thank you.
Your printf("%s ",a); statement outputs a space after each string it outputs, regardless. You need to instead output a space only if there are going to be more strings. Equivalently (and easier), output a space before each string if it is not the first:
int first = 1;
while (scanf("%s",a) != EOF) {
if (!first) printf(" ");
printf("%s",a);
first = 0;
}
also, you never output a newline, despite describing a newline as being output...
A little note about independent learning:
The first time you use any standard library function, you should read its corresponding manual at least once. The reason is that as soon as you know about any issues (or helpful features), you'll know to avoid those issues (or use the helpful features). Reading the manual really helps you learn the full potential and avoid the pitfalls of C.
The first time you read something, you may not entirely understand it. Don't let that bother you; Just keep reading. If this happens, I suggest that you read it again once you're at the bottom of the page, and try to understand it completely the second time. Please, read the fscanf manual at least once.
That's out of the way, now. You might notice little ^ superscripts here and there. My intention is to annotate facts that you would have learnt by reading and understanding the manual I linked to above.
The format specifier %s discards as much whitespace as possible prior to attempting to consume as much non-whitespace as possible^. Suppose you were to input something with four leading bytes of whitespace, your output wouldn't have those four leading bytes of whitespace.
"This uses four spaces" translates to "This uses four spaces".
It seems logical that this violates your program specification, which extends your problem^. Perhaps you should be using the %[ format specifier, instead^. In that case, an empty line would be considered a match failure and the destination for the line would be indeterminate, so you'd need to handle the return value of scanf better^ (which I encourage you to do, anyway).
#include <stdio.h>
int main(void) {
char line[512];
int x;
do {
x = scanf("%511[^\n]", line);
if (x == 0) {
/* Match failure caused by inputting an empty line. Print empty line? */
putchar('\n');
}
else if (x == 1) {
/* Success. */
puts(line);
}
/* Read and discard the remainder of a line */
do { x = getchar(); } while (x >= 0 && x != '\n');
} while (x != EOF);
}
From the length of this loop and the simplicity of the problem, you might reason that using scanf for this is probably the wrong way to solve the problem. You can eliminate the line array, the calls to scanf and the resulting scanf error checking by using a derivation of the inner-most loop. Something like this looks nice:
#include <stdio.h>
int main(void) {
int c;
do {
c = getchar();
} while (c >= 0 && putchar(c) >= 0 && c != '\n');
}
PS. There's also a manual for getchar, putchar and many other standard things.

C - error: ignoring return value of scanf?

I'm only a few days into C programming, so I am not quite sure what's wrong with this code:
#include <stdio.h>
int main(int argc, char * argv[]) {
int sides;
printf("Please enter length of three sides :\n");
scanf("%d", &sides);
return 0;
}
The error message I receive is as follows:
ignoring return value of scanf
What am I doing wrong here, and what can I do to fix it?
You might code
if (scanf("%d", &sides) >0) {
printf("you want %d sides.\n", sides);
}
else printf("You did not enter any number.\n");
The scanf function (please follow the link) has several roles
it is expecting some input and could modify the variables you passed by address to it
it is returning the number of successfully input items
It's a warning that stops your compiler from performing it's task (too strict settings). Check the return value of the scanf() function for errors and the warning should disappear.
Return Value
On success, the function returns the number of items
successfully read. This count can match the expected number of
readings or fewer, even zero, if a matching failure happens. In the
case of an input failure before any data could be successfully read,
EOF is returned.
scanf returns the number of "items", i.e. values passed both in the format string (a single item is e.g. %d, %c and so on), and in the subsequent arguments to scanf, for example, to read two integers separated by comma and space, you would use:
int x, y;
int items = scanf("%d, %d", &x, &y);
assert(items == 2);
I've already spoiled what my suggestion will be above - instead of adding unused variables, if you just want to read it, add an assertion:
#include <assert.h>
/* ... */
assert(scanf("%d", &sides) > 0);
/* ... */
Unfortunately, assert(scanf("%d", &sides)); is not enough, because of EOF (this will return -1). It would be really elegant.
I think this is the way to go, if you don't want to continue your program with an uninitialized variable (sides) in this case.
Alternatively, you can capture scanf's result to a variable, and handle it gracefully like in the other answers.
You don't capture the return value of scanf in a variable. It's a count (as an integer) of how many characters were read, so if that's important to you, then it may be good to capture it.

Am I using scanf incorrectly?

Each line of input is a line with a command followed by numbers (except in the exit case).
I cannot figure out what I am doing wrong. This segment is looking for the store command and then does the action store entails:
char command[20];
while(strcmp(command, "exit") != 0)
{
/*scans for command strings inputted*/
scanf(" %s", command);
/* handles store command*/
if(strcmp(command, "store") == 0)
{
memory[0] = 1;
scanf("%d %d %d %d %d", &startx, &starty, &finishx, &finishy, &number);
for( i = startx; i < finishx; i++)
{
for(j = starty; j < finishy; j++)
{
square[i][j] = number;
}
}
}
}
Yes, you are using it wrongly(a). The line:
scanf(" %s", command);
has no bounds checking on the input. If someone enters more than 19 characters into your program, it will overflow the char command[20] and result in undefined behaviour.
The two major problems with scanf are:
using it with an unbounded %s since there's no way to control how much data is entered. My favourite saying is that scanf is to scan formatted information and there's not much less formatted than user input.
not checking how many items were scanned - it's possible to scan less than you expected.
If you want to do it right, see here. It uses fgets to get a line, protecting from buffer overflows and detecting problems.
Once you have the line as a string, you can safely sscanf it to your heart's content, since you know the upper bound on the length and you can always return to the start of the string to re-scan (not something easy to do with an input stream).
Other problems with your code include:
The initial strcmp on an uninitialised buffer. It could actually be (arbitrarily) set to exit which would cause your loop not too start. More likely is that it's not a valid C string at all, meaning strcmp will run off the end of the buffer. That won't necessarily end well.
No checking that all your numeric items were entered correctly. You do that by checking the return value from scanf (or sscanf should you follow my advice on using the rock-solid input function linked to here) - it should be five but entering 1 hello 2 3 4 would result in a return code of one (and all but startx unchanged).
No range checking on input. Since square has limited dimensions, you should check the inputs to ensure you don't write outside of the array. That's both numbers that are too large and negative numbers.
(a) Syntactically, what you have is correct. However, from a actual semantic point of view (i.e., what you mean to happen vs. what may happen), there's a hole in your code large enough to fly an Airbus A380 through :-)

Resources