>I disagree. You have to have all kinds of special code to get scanf() to work properly.
Only if you aren't using scanf for what it was designed in the first place. When you have to write workaround code to get a function to work, you're using the wrong tool or using it the wrong way. This is probably not the best place to critique your link, but I'll do it anyway. I'll quote you (WaltP) with > and the link with quote tags.
Admittedly, I've never studied the scanf() fully.
I'm not sure if he's being humble or really hasn't studied scanf fully. As some background, I, on the other hand, have studied it completely for many years, and also implemented it completely for both C89 and C99 several times (each in a different way). I like to think I have a strong understanding of how scanf works, and that's what guides my opinions. But that's probably also going to give me more of a liberal perspective because I'm not afraid of the function in any way as it holds no mysteries for me. :)
They want to read a character from the keyboard so they very logically use the format string "%c". Then wonder why the next read is messed up. How are they supposed to know that after the character was read there was a \n left behind?
While he's not wrong (assuming it's a he), getchar has the same problem. This is related to how streams work, not scanf specifically.
To examine the size of the scanf() function, I built the following programs to check the executable sizes.
Sure, scanf does a lot. But so does printf (probably even more so), and I don't see this guy railing on printf for taking up instruction space. ;) This whole part of the argument strikes me as unnecessary fluff that gets in the way of the real issues and tries to bolster the argument.
So use getchar() instead. It's easier and designed exactly for the task at hand.
Assuming you only need a character and there's no special formatting, I agree that getchar is the better choice. But the author is talking about a strict subset of how you might use the %c specifier.
Just remember to clear the keyboard buffer. In either case, the trailing \n will be left in the input stream.
Yea, if you have to clear the "keyboard" buffer like that, something is wrong with your input process. It's not scanf or getchar that left garbage in the stream, but the programmer's sloppy use of them. A good input method won't throw anything away unconditionally, which makes that discarding loop little more than a kludgy hack.
scanf() and gets() have the exact same problem with memory overrun.
The difference is that scanf can be fixed while gets can't. If you aren't providing a minimum field width for %s, you shouldn't be using it in the first place. Lack of understanding about the specifiers doesn't make scanf bad. You can do the same thing with fgets if you don't know what you're doing:
char s[10];
if ( fgets ( s, INT_MAX, stdin ) != NULL ) {
/* ... */
}
I usually teach %s as requiring a field width and if you omit the field width, you get undefined behavior. That's not far from the truth, so it tends to work well. :)
It is therefore necessary after every scanf() call that reads non-character data (ints, floats, etc.) to check the return status to see if it worked.
It's necessary after every input function at all to check the return status to see if it worked. That's programming 101, not an issue with scanf specifically.
Based on these tests, I would say scanf() just failed as a viable input mechanism.
In much the same way I could argue that this use of fgets fails as a viable input mechanism because you can't tell if there's more input on the line:
char s[10];
if ( fgets ( s, sizeof s, stdin ) != NULL ) {
char *nl = strchr ( s, '\n' );
if ( nl != NULL )
*nl = '\0';
/* ... */
}
You might say "Sure, but you're using fgets wrong if you want to check for long lines!", and I'd retort with "And you're using scanf wrong if you want to error check poorly formatted user input". scanf was designed for formatted input. User input can't be guaranteed to be formatted in the console. This is a case of the wrong tool being used for the job.
So individual parsing of the user's keyboard input is the only way to make your input bulletproof. You have to take over and use tried and true conversion functions to read numbers and not rely on scanf() to do the job.
This is orders of magnitude more complicated, more error prone, and could easily result in the very code bloat that the author complains about for scanf. The strto* functions aren't exactly trivial, and you'd end up writing application specific parsing algorithms each and every time you want a number because any generic solution would cause the same problems that you're "solving" by not using sscanf.
scanf() is not designed for production code, but for creating test programs to see if other aspects of the program are working. scanf() will replace the complicated input routines so that other modules can be exercised and perfected. Then the scanf() is replaced with the real input function that gets the actual data from a file, a device, wherever the actual data is stored.
scanf is designed for production code, but it's not a panacea. This author seems to think that scanf is a jack of all trades meant to be used everywhere, when it really isn't.
This will keep reading until there is a non-whitespace character entered, effectively clearing the buffer from previous scanf() calls.
Why the loop? What if "whitespace" for the locale isn't limited to those characters? Yet you can still use scanf to solve the problem without the bugs or complexity (or poor style by assuming a character set) by adding a leading space to the format string:
scanf ( " %c", &ch );
>Best to switch to fgets() and be safe.
fgets has problems too. You're not going to find an input mechanism to switch to that doesn't have confusing issues.
>Then for formatted input use sscanf() to break the input apart.
All of the scanf problems are still there with sscanf. You don't have to deal with extra characters on the stream, but you still have to deal with error checking the source outside of sscanf. So if you're getting the string from the user, for sscanf to work properly, you'd have to fit a validation step between fgets and sscanf to verify that the string is in the correct format (or if sscanf fails).
Despite what the regulars here say, switching from scanf to fgets/sscanf isn't going to solve all of your problems with scanf.