>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.

Recommended Answers

All 7 Replies

>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.

My point exactly. scanf() is dangerous and inadequate reading strings (only reads to whitespace, and can easily blow your buffer if no spaces are in the input)
It's overkill if you want to read a single character. Why bring in the code for translating %d, %i, %f, etc. when getchar() does exactly the same thing in a small footprint?
After reading a number, the input stream still has characters left and must be cleared if reading strings next. This requires "fix-up" code.

I am of course targeting those that learn scanf() in school. Instructors never seem to explain any of the problems associated with the function, letting their students flounder. They are not taught the formatting characters required to use scanf() cleanly. That's why all the forums are inundated with questions about skipped input, partial input, and looping input.

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. :)

Good for you. I on the other hand have found scanf() to be inadequate for production code because of it's lack of error checking, ability to bulletproof, and convoluted format specifiers attempting to correct anomalies. Therefore I use it only for testing.

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.

How many Waltina's do you know :icon_rolleyes:
Yes, getchar() has the same problem (but it's less hidden IMO). My complaint is the function footprint in this case. I see a lot of code that uses only one scanf() , and that's to read a single character.

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.

Formatted output is necessary, therefore so is printf() . Yes, formatted input is necessary, but scanf() has too many problems. sscanf() simply clears up the biggest problems. But using fgets() first allows the input to be checked before translating bad data. Can't do that with scanf() .

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.

No it's not. It's the programmer's job to correct the sloppy function. The fact is that stuff is left in the input buffer. The programmer must understand that and "kludge" up his program to clear it. It's not a sloppy programmer...

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.

That's my complaint. Most instructors seem to never teach enough, letting the student's hang themselves.

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.

Yeah, but our friend does not return an error status. It simply returns the number of specifiers translated. If you are reading a %d field and enter 23R you get no indication of an error. It works fine. I suggest that mistyping should cause an error if an illegal character is seen.

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.

There's just so much wrong with what you just said.... I'l comment on this at the end.

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.

That's also why I don't use strto*() functions. Parsers are the most bulletproof functions available -- because you write them to exacting specifications. And they can be added to a library for easy retrieval.

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.

What?!?! Where do you get the idea I think it is "meant to be used everywhere"? I thought I was clear -- only during development because you control the input.

Despite what the regulars here say, switching from scanf to fgets/sscanf isn't going to solve all of your problems with scanf.

No, but it solves most of them...

------------

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".

But scanf() is meant for user input! Meatware sitting at a keyboard! Isn't that the definition of "poorly formatted input?" At least with fgets() you have the chance to test the input before translation and actually doing error cleanup before it's too late.

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.

My point exactly!!!! Since scanf() is designed for user input, and user input cannot be guaranteed to be formatted, then scanf() is the wrong tool for the job! Doh!

Allow me to reiterate:

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.

You said it yourself.... :icon_wink:

I didn't realize you were the one who wrote those articles. :)

scanf() is dangerous and inadequate reading strings (only reads to whitespace, and can easily blow your buffer if no spaces are in the input)

%s isn't the only way to read strings, but I'll take your point as talking only about %s without a field width.

It's overkill if you want to read a single character.

So is printf for a lot of ways it's used. I don't see that as a flaw in printf, do you? :)

It's not a sloppy programmer...

If you're using the function incorrectly, and then kludging up your code to fix it, I'd call that sloppy.

If you are reading a %d field and enter 23R you get no indication of an error.

Why is that an error? Did it occur to you that the R might be there in anticipation of the next input request? One of my biggest pet peeves is code that always clears the line to remove "garbage", when what I typed was most certainly not garbage.

Parsers are the most bulletproof functions available -- because you write them to exacting specifications.

So you advocate ignoring the standard library and writing your own parsers from scratch?

What?!?! Where do you get the idea I think it is "meant to be used everywhere"?

Here:

scanf() is a Jack of all trades function. It has been designed to handle as many input types as possible, from simple characters to scientific notation and complex values. And because of this, the design seems lacking in some areas.

I don't know the key to success, but the key to failure is trying to please everybody. -- Bill Cosby

That's how I feel about scanf().

No, but it solves most of them...

Using sscanf doesn't correct the majority of the issues you raise in your articles.

But scanf() is meant for user input! Meatware sitting at a keyboard!

C doesn't recognize meatware or keyboards. scanf is meant for formatted input from stdin. It happens that stdin is often connected to an interactive input device these days, but I would claim that the design of scanf is meant more for filter programs where input is piped or redirected from a formatted source.

I didn't realize you were the one who wrote those articles. :)

Surprise!!!!

So is printf for a lot of ways it's used. I don't see that as a flaw in printf, do you? :)

I didn't say it's a flaw in scanf(). Reading a char is necessary and useful when reading formatted input that contains varied field types, including char. But for reading only 1 char, that's my argument. Just as using printf() to output only a single char is IMO a misuse.

If you're using the function incorrectly, and then kludging up your code to fix it, I'd call that sloppy.

Given what a student programmer generally knows, how is using scanf() to read an int and leaving the return in the buffer sloppy on the programmer's part? Code must be added to clear the buffer or a convoluted format specifier must be handed to them. And I'll bet many, if not most, instructors have no idea how to clear the buffer using scanf() while reading a number. Especially with ill-formatted input via user-error.

Why is that an error? Did it occur to you that the R might be there in anticipation of the next input request? One of my biggest pet peeves is code that always clears the line to remove "garbage", when what I typed was most certainly not garbage.

Since I came up with the example, you can be absolutely assured that the R in "23R" is an invalid input, not meant for the next input.

So you advocate ignoring the standard library and writing your own parsers from scratch?

For bullet-proof input, especially from the keyboard, absolutely. If you know for a fact that the input will be formatted correctly each and every time from stdin, scanf() is fine. But it just isn't so when people are involved in generating the input.

C doesn't recognize meatware or keyboards. scanf is meant for formatted input from stdin. It happens that stdin is often connected to an interactive input device these days, but I would claim that the design of scanf is meant more for filter programs where input is piped or redirected from a formatted source.

Often? These days? Are you saying that stdin at during the beginning development of C was never attached to a keyboard? During development of the language and run-time all input was piped into the test code? And attaching it to the keyboard is rare -- and a new development? I know you;re not that naive... :icon_wink:

Yes, C is a 'deviceless' language. But stdin has always been attached to the keyboard from the beginning, and can be redirected from elsewhere. I therefore agree with you, scanf() was not meant for user input but formatted input. And because of that it's just barely adequate for user/keyboard input. And C by definition and design was not meant for user interaction. Unfortunately, it does need to interact, and scanf() has been the function of choice.

I still say there are better ways to get user input.

But for reading only 1 char, that's my argument. Just as using printf() to output only a single char is IMO a misuse.

Just so we're clear. :)

Given what a student programmer generally knows, how is using scanf() to read an int and leaving the return in the buffer sloppy on the programmer's part?

Sloppy code is sloppy code, regardless of whether it's spawned from negligence or ignorance.

And I'll bet many, if not most, instructors have no idea how to clear the buffer using scanf() while reading a number. Especially with ill-formatted input via user-error.

That's a safe bet. ;)

Since I came up with the example, you can be absolutely assured that the R in "23R" is an invalid input, not meant for the next input.

I guess I can't argue with carefully contrived examples.

If you know for a fact that the input will be formatted correctly each and every time from stdin, scanf() is fine.

Not just scanf. If I don't know for a fact that the input will be formatted correctly each and every time. It looks like you want me to ignore scanf, sscanf, strto*, ato*, and any other existing parsing mechanism in favor of my own hand rolled application-specific parser.

I know you;re not that naive...

And I know you know exactly what I meant and was trying to do by using non-absolute wording.

I still say there are better ways to get user input.

I think you got mixed up somewhere along the line. I'm not trying to convince you that scanf is suitable for user input. I'm addressing your attacks on scanf by showing you that it's the user of the function and not the function itself that's the problem. Don't put a blanket ban on something just because people don't know how to use it. Teach them how to use it.

Certainly I am grateful to Ptolemy and WaltP for continuing this conversation; if only because indirectly expose a fundamental principle of the heritage of the C language. Which is that “C was developed by people that knew for people that know” or in other words “created by programmers for programmers


Therefore someone that wants to be a programmer; and I don't mean “programmer as: I compiled this
code and it works”, ( for that we always have Visual Basic ); needs to stand in borrowed knowledge.
At least until understanding comes along.


For that reason I still stand behind my words:

>Haven't you figure out yet why you should avoid using scanf?
To which Ptolemy responded:
>I don't think you should avoid scanf. I think you should learn how it works so that you can use it intelligently.

Never was my intention to propose the avoidance all together of the usage of scanf, but rather a direct hint to the OP that perhaps “he/she was not ready to use it”. Using a wore out cliché : “There is a time and place for using scanf” And the time is not when you don't know when you need to use a loop, nor how to use a break statement. And the place is not when you don't know how to control the flow of the program, nor how to deal with user input.


As it has been said in the past: “You need to learn how to walk before you can run”
And this all too cavalier usage of scanf to gather inputted information doesn't help in the pursue of a secure footage. Too many “gotchas”, even for knowledgeable people.
As an example I would like to bring to attention a post from the original thread that started this dialog:


As a response to my posted snippet;

/* skunk.c */
 #include <stdio.h>
 

 int main( void )
 {
     int i = 0;
     char c = '\0';
      
     fputs( "Enter an integer: ", stdout );
     fflush( stdout );
      
     while( scanf( "%d%c", &i, &c ) != 2 )
     {
         while ( ( c = getchar() ) != '\n' && c != EOF );
         printf( "Invalid input! Please enter the value again: " );
         fflush( stdout );
     }
     printf( "i = %d\n", i );
      
     getchar();
     return 0;
 }

Ptolomy wrote:
>while( scanf( "%d%c", &i, &c ) != 2 )
There's really no point reading c here because the next thing you do is getchar into c.

Not seeing that there was a point to it. Which it was to clear the buffer input as soon as possible in case that the right answer was entered. Knowing that in the event of a successful input the “enter key” would be there; why not to get rid of it, and clear the way for any other gathering input function or call
to collect the right data? In this case so the last getchar() would be able to pause the program as intended.
Instead attention was only shown to the getchar inside the while loop.

As I said too many “gotchas”.

I guess I can't argue with carefully contrived examples.

Good... Because this is exactly the type of input that has caused massive brain hemorrhages in students on help forums. :icon_smile:

Not just scanf. If I don't know for a fact that the input will be formatted correctly each and every time. It looks like you want me to ignore scanf, sscanf, strto*, ato*, and any other existing parsing mechanism in favor of my own hand rolled application-specific parser.

I never said ignore them. Only gets(). And if you noticed, I also suggested using sscanf() . I personally don't like strto*() functions. But I do use ato*() functions, even with their potential traps. And that's because in those programs I've already validated the input and know the functions won't cause problems. You simply can't validate input with scanf() . But you can with sscanf() .

I think you got mixed up somewhere along the line. I'm not trying to convince you that scanf is suitable for user input. I'm addressing your attacks on scanf by showing you that it's the user of the function and not the function itself that's the problem.

Two responses:
#1:
Agreed. And my point is that students are users that simply do not understand the function and instructors should either:
1) stop teaching it early
2) explain it has problems and use it sparingly -- if things don't work you've been warned
3) teach the darn thing! Give the student a full day on format specifiers and the anomalies the function causes.
They do none of the above, it seems, from the questions posted here.

#2:
Then what the heck are you arguing about? "I'm not trying to convince you that scanf is suitable for user input" is exactly my point. All I'm talking about is user input, because that is what scanf() is used for 99% of the time. :icon_rolleyes:

Certainly I am grateful to Ptolemy and WaltP for continuing this conversation; if only because indirectly expose a fundamental principle of the heritage of the C language. Which is that “C was developed by people that knew for people that know” or in other words “created by programmers for programmers

You're welcome. These discussions are fun, and I always glean new insights. Sometimes I even change my mind :icon_wink:

As it has been said in the past: “You need to learn how to walk before you can run”
And this all too cavalier usage of scanf to gather inputted information doesn't help in the pursue of a secure footage. Too many “gotchas”, even for knowledgeable people.

Yes...

Despite what the regulars here say, switching from scanf to fgets/sscanf isn't going to solve all of your problems with scanf.

Nope. But it easily passes the 80/20 rule for acolytes.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.