scanf() vs gets()

T

Teh Charleh

OK I have 2 similar programmes, why does the first one work and the second does
not? Basically the problem is that the program seems to ignore the gets call if
it comes after a scanf call. Please anything even a hint would be really
helpful, I cant for the life of me see why the 2nd prog wont work...

gets before scanf



code:---------------------------------------------------------------------
-----------
#include <stdio.h>

int a;
char aaa[50];

main()
{
printf("Enter a string\n");
gets(aaa);
printf("%s\n",aaa);

printf("Enter a number\n");
scanf("%d",&a);
printf("%d",a);
}
--------------------------------------------------------------------------
------


scanf before gets


code:---------------------------------------------------------------------
-----------
#include <stdio.h>

int a;
char aaa[50];

main()
{
printf("Enter a number\n");
scanf("%d",&a);
printf("%d",a);

printf("Enter a string\n");
gets(aaa);
printf("%s\n",aaa);
}
 
B

Ben Pfaff

#include <stdio.h>

int a;
char aaa[50];

Should be local variables. No reason for them to be global.

You should write `int main(void)' explicitly.
{
printf("Enter a number\n");
scanf("%d",&a);
printf("%d",a);

printf("Enter a string\n");
gets(aaa);
printf("%s\n",aaa);

You should write `return 0;' explicitly here.

The problem is that scanf()'s %d specifier will read an integer
and then stop. It won't read following white space, including
the new-line character that corresponds to you pushing the Enter
key. Then gets() will read the new-line character and provide an
empty string as aaa[].

By the way, don't use gets(). It cannot be used safely, except
by Dan Pop.
 
M

Mike Wahler

Teh Charleh said:
OK I have 2 similar programmes, why does the first one work and the second does
not? Basically the problem is that the program seems to ignore the gets call if
it comes after a scanf call. Please anything even a hint would be really
helpful, I cant for the life of me see why the 2nd prog wont work...

gets before scanf



code:---------------------------------------------------------------------
-----------
#include <stdio.h>

int a;
char aaa[50];

main()

int main(void)
{
printf("Enter a string\n");
gets(aaa);

*Never* use 'gets()'. Never, never, never, ever use it. Ever.
Period. It *cannot* be used safely. Ever. Use 'fgets()' instead.

More below.
printf("%s\n",aaa);

printf("Enter a number\n");
scanf("%d",&a);
printf("%d",a);

return 0; /* main() is *required* to return an int */
}
--------------------------------------------------------------------------
------


scanf before gets


code:---------------------------------------------------------------------
-----------
#include <stdio.h>

int a;
char aaa[50];

main()

int main(void)
{
printf("Enter a number\n");
scanf("%d",&a);

'scanf()' will first skip over any whitespace characters, then
start extracting characters until a nonwhitespace character is
encountered (which is not extracted), or an error (e.g.
if a nondigit character is read for '%d' specifier) occurs,
or end of stream is reached.

Assuming that your input is correct for %d, and that the last
input character (a digit) was followed by a newline (e.g.
pressing 'Enter' key on a keyboard), then the digits will
be extracted, converted to binary and the resulting value
stored in the object 'a'. The newline character which caused
'scanf()' to stop parsing will still be in the input stream,
and will be supplied to the next input request.

I believe this is what's happening in your case.
printf("%d",a);

printf("Enter a string\n");
gets(aaa);

If my above assumptions hold, then this invocation of 'gets()'
will retrieve (and discard) the waiting newline character, store
a zero character in aaa[0], and return.
printf("%s\n",aaa);

return 0;

Your problem seems to result from not understanding that 'scanf()'
does not extract any whitespace characters which might occur after
an extracted data item (e.g. an 'int', 'float', etc.).

And again, *NEVER* use 'gets()'. EVER.

You should also check the return value from 'scanf()' for
errors.

Although I've explained it here, note that the answer to your
question is in the C FAQ, which should be checked before posting:
http://www.eskimo.com/~scs/C-faq/q12.18.html

Also the sentiments I expressed about 'gets()' are also in the
C FAQ:
http://www.eskimo.com/~scs/C-faq/q12.23.html

-Mike
 
T

Teh Charleh

The problem is that scanf()'s %d specifier will read an integer
and then stop. It won't read following white space, including
the new-line character that corresponds to you pushing the Enter
key. Then gets() will read the new-line character and provide an
empty string as aaa[].

This makes perfect sense and explains the behavior of my problem. This now begs
the question how do I go about telling gets() to ignore the newline charecter
input after the scanf...
By the way, don't use gets(). It cannot be used safely, except
by Dan Pop.

My book here C programming in easy steps is definately recommending it over
scanf if I want to input more than one word as a string!
 
B

Ben Pfaff

My book here C programming in easy steps is definately recommending it over
scanf if I want to input more than one word as a string!

This is in the FAQ.

12.23: Why does everyone say not to use gets()?

A: Unlike fgets(), gets() cannot be told the size of the buffer
it's to read into, so it cannot be prevented from overflowing
that buffer. As a general rule, always use fgets(). See
question 7.1 for a code fragment illustrating the replacement of
gets() with fgets().

References: Rationale Sec. 4.9.7.2; H&S Sec. 15.7 p. 356.

Actually, your original question is in the FAQ too (I had
forgotten this):

12.18: I'm reading a number with scanf %d and then a string with
gets(), but the compiler seems to be skipping the call to
gets()!

A: scanf %d won't consume a trailing newline. If the input number
is immediately followed by a newline, that newline will
immediately satisfy the gets().

As a general rule, you shouldn't try to interlace calls to
scanf() with calls to gets() (or any other input routines);
scanf's peculiar treatment of newlines almost always leads to
trouble. Either use scanf() to read everything or nothing.

See also questions 12.20 and 12.23.

References: ISO Sec. 7.9.6.2; H&S Sec. 15.8 pp. 357-64.
 
T

Teh Charleh

*Never* use 'gets()'. Never, never, never, ever use it. Ever.

Im kinda getting the impression that this is an extremely unpopular function :)
Use 'fgets()' instead.

Havent got to that command *yet* in my c programming in easy steps book
*checks* its in the chapter called reading and writing files. Ill be getting to
it soon.
I believe this is what's happening in your case.

Yes, me too!
Your problem seems to result from not understanding that 'scanf()'
does not extract any whitespace characters which might occur after
an extracted data item

No I understood that, i didnt realise that the newline would held somewhere and
passed through to the next input request.
And again, *NEVER* use 'gets()'. EVER.

Yet more anti gets() ;) Im looking forward to reading about fgets()
Although I've explained it here, note that the answer to your
question is in the C FAQ, which should be checked before posting:
http://www.eskimo.com/~scs/C-faq/q12.18.htm

Thanks, this is my first ever visit to the newsgroup - Ill take a nice long
look at the faq, before I think baout asking another question :)
 
T

Teh Charleh

I found this - its my favorite way around the problem :

Another way would be to use %*c in the format string. The * tells it to read
from the input buffer, but not assign it to any variable.

Another solution would be to assign the newline charecter to a dummy variable
 
M

Mike Wahler

Teh Charleh said:
The problem is that scanf()'s %d specifier will read an integer
and then stop. It won't read following white space, including
the new-line character that corresponds to you pushing the Enter
key. Then gets() will read the new-line character and provide an
empty string as aaa[].

This makes perfect sense and explains the behavior of my problem. This now begs
the question how do I go about telling gets() to ignore the newline
charecter

Why did you ignore Ben's (correct) advice to not use 'gets()'?

In either case, you cannot 'tell' 'gets()' to do anything other
than what it is designed to do. What it will do is extract any
available characters until a newline character is extracted (and if
extracted it will be discarded), or an error occurs, or end of
stream is reached. The extracted characters are stored starting
at the address supplied as 'gets()'s argument, each successive
character being stored in the adjacent (increasing) address. A zero
character is stored at the address one higher than the last extracted
character.

<*IMPORTANT*>
There is absolutely no way to cause 'gets()' to limit the number of
characters it stores, so it is impossible to protect against it
overflowing a buffer. Thus the advice to NEVER use 'gets()'.
<*IMPORTANT*>

You cannot change the defined behavior of standard library functions.
You must choose which function(s), and combinations of functions will
achieve your purpose. This is done by consulting the documentation
for the functions to determine exactly how they behave.

BTW 'fgets()' *does* extract a newline if one exists in the input
stream. It also stores it in the buffer. You can leave it there
or remove it if you like. 'fgets()' also provides a mechanism for
protecting against buffer overflow. Read about it.
input after the scanf...


My book here C programming in easy steps is definately recommending it

The vast majority of programming books for sale are simply incorrect.
See www.accu.org for peer reviews and recommendations.
over
scanf if I want to input more than one word as a string!

Use 'fgets()'.

Read The FAQ. http://www.eskimo.com/~scs/C-faq/top.html
(My other post in this thread has pointers to information
therein about your exact questions).

But Read The Entire FAQ:
http://www.eskimo.com/~scs/C-faq/top.html

All Of It.
You'll Be Glad You Did.

-Mike
 
B

Ben Pfaff

The problem is that scanf()'s %d specifier will read an integer
and then stop. It won't read following white space, including
the new-line character that corresponds to you pushing the Enter
key. Then gets() will read the new-line character and provide an
empty string as aaa[].

This makes perfect sense and explains the behavior of my problem. This now begs
the question how do I go about telling gets() to ignore the newline charecter
input after the scanf...

Just skip past the new-line character yourself. You can use
scanf() in a clever fashion to do this, or you can write a simple
loop:

for (;;) {
int c = getchar ();
if (c == '\n' || c == EOF)
break;
}
 
T

Teh Charleh

Why did you ignore Ben's (correct) advice to not use 'gets()'?

Wasnt ignoring it :) just wanted to see how to get around the original problem
I was posed with (I found out about %*c, I think that ill do the job!) But hey
this short thread has greatly helped with my understanding of scanf, gets and
the potential of fgets which is still 2 chapters away!
The vast majority of programming books for sale are simply incorrect.
< Someone should maybe think about writing a book that is actually correct -Id
buy it!
Use 'fgets()'.

From what Ive been hearing, it look as though fgets is definately the way
forward, thanks :)
 
M

Mike Wahler

Teh Charleh said:
Wasnt ignoring it :)

He (and I) advised against its use. Your next post asked
how to continue to use it. Looks like you ignored it to me. :)
just wanted to see how to get around the original problem
I was posed with (I found out about %*c, I think that ill do the job!)

It might, for a while. But try some more complex input, especially
intentionally invalid, and e.g. 'too long' input.
But hey
this short thread has greatly helped with my understanding of scanf,
Good.

gets

Only one thing need be understood about 'gets()': It should *NEVER*
be used. Ever.
and
the potential of fgets which is still 2 chapters away!

There's no law requiring you to read all text in a book
in sequential order. :)
correct -Id

Many have. See www.accu.org as I've already advised.
buy it!


From what Ive been hearing, it look as though fgets is definately the way
forward, thanks :)

It may be, it may not. It depends upon your goal. But 'fgets()'
can be used safely, 'gets()' cannot. (And it's not all that simple
to use 'scanf()' safely, either, but it can be done).


-Mike
 
K

Kevin Goodsell

Teh said:
Im kinda getting the impression that this is an extremely unpopular function :)

Not really. And that's the problem. It's a function that shouldn't
exist, yet many people still use it. Look up the Morris Worm. This is
only one the earliest and most widely-known examples of gets() causing
serious problems. I doubt any single function/subroutine/procedure has
caused anywhere near as much damage and cost as much money as gets() has.

-Kevin
 
M

Mark McIntyre

On 30 Nov 2003 20:28:41 GMT, in comp.lang.c ,
(e-mail address removed) (Teh Charleh) wrote:

(please don't snip attributions, it helps keep posts sensible)

Ben pfaff wrote
My book here C programming in easy steps is definately recommending it over
scanf if I want to input more than one word as a string!

Your C book is teaching you Bad C, probably because its an
introductory work.

gets() is a lethal function, rumoured to be responsible for more virus
propagations even than the Evil Empire. Don't use gets(). Use fgets(),
which is bufferlength safe, and then sscanf the string.
 
C

CBFalconer

Teh said:
.... snip ...


My book here C programming in easy steps is definately recommending
it over scanf if I want to input more than one word as a string!

Never use gets. See the FAQ for reasons. Avoid scanf for
interactive work. You can use sscanf (or other routines) to parse
what a gets /replacement/ provides.

Any replacement for gets requires something extra from the
programmer to control it, such as supplying a buffer size
(fgets). Richard Heathfield has a readline routine, and I provide
a ggets() routine. ggets requires only that you supply the
address of a pointer, and eventually free the memory that pointer
points to. It is built upon the use of fgets. You can get the
source code and usage examples at:

<http://cbfalconer.att.net/download/>

Please tell us the name, author, publisher etc. of your C book so
that we can recommend burning it.
 
R

Richard Heathfield

Teh said:
This makes perfect sense and explains the behavior of my problem. This now
begs the question how do I go about telling gets() to ignore the newline
charecter input after the scanf...


My book here C programming in easy steps is definately recommending it
over scanf if I want to input more than one word as a string!

Then your book was written by someone who is not very experienced in C. It
sounds like Herbert Schildt's handicraft, in fact.

See http://users.powernet.co.uk/eton/c/fgetdata.html for a more detailed
discussion of scanf, gets, fgets, and so on.
 
R

Richard Heathfield

CBFalconer said:
Never use gets. See the FAQ for reasons. Avoid scanf for
interactive work. You can use sscanf (or other routines) to parse
what a gets /replacement/ provides.

Any replacement for gets requires something extra from the
programmer to control it, such as supplying a buffer size
(fgets). Richard Heathfield has a readline routine,

It's actually called fgetline(). I'll tell you what - I'll link to your page
if you link to mine. Deal?
and I provide
a ggets() routine. ggets requires only that you supply the
address of a pointer, and eventually free the memory that pointer
points to. It is built upon the use of fgets. You can get the
source code and usage examples at:

<http://cbfalconer.att.net/download/>

Hmmm. Seems to be down. I'll add the link once I know the exact page (which
I can't find out while the server is down, obviously - unless you tell me
here).
Please tell us the name, author, publisher etc. of your C book so
that we can recommend burning it.

Please hold the burn. The last two occasions I recall where someone said
something similar, the books in question turned out to be <cough> "C
Unleashed" and "The C Programming Language" and, in each case, the newbie
had misunderstood the book.
 
P

Peter Nilsson

Mike Wahler said:
return 0; /* main() is *required* to return an int */

No user defined function is actually _required_ to return a value in C90 or
C99, main() or otherwise.

Non-void function definitions in C90 are even allowed to use...

return;

....to exit. C99 [possibly C94/5?] added the criteria that non-void functions
must have an expression following the 'return', however the following subtle
weakness was left in:

"[For general functions,] If the } that terminates a function is reached,
and the value of the function call is used by the caller, the behavior is
undefined."

Although, in the case of a hosted environment main() definition, the return
value on reaching } is unspecified in C90, and 0 in C99.

All that said, typing return 0; in main isn't all that hard, especially
given the maximal robustness is supplies [broken historic implementations
notwithstanding.]
 
M

Mark McIntyre

No user defined function is actually _required_ to return a value in C90 or
C99, main() or otherwise.

In c89, the relevant section of the standard said "It shall be defined
with a return type of int". Not much room there I feel for main() to
return other than an int. Lets not start another debate about the
meaning of the "or in some other..." part.

In c99, main is also required to return int. However if the user
suppplied code doesn't do it, then the compiler shall behave as though
zero were returned.

Snip stuff about nonvoid functions being able to return nothing. I
don't believe its quite valid but I'm not going to bother trawling
through the standard to prove it. Plus FWIW every compiler I've ever
used emits an error when a nonvoid function fails to return a value.
This is hardly conclusive, but strong circumstantial evidence.

And in any events, failing to return a value from a fn you explicitly
declared to return a value is just plain bad programming.
 
P

Peter Nilsson

Mark McIntyre said:
In c89, the relevant section of the standard said "It shall be defined
with a return type of int". Not much room there I feel for main() to
return other than an int.

I never said main could or should have a return type other than int. I said
it needn't return a value, i.e. it needn't have a return statement.

....
Snip stuff about nonvoid functions being able to return nothing. I
don't believe its quite valid but I'm not going to bother trawling
through the standard to prove it.

Well, ignorance is bliss...
Plus FWIW every compiler I've ever
used emits an error when a nonvoid function fails to return a value.

A 'diagnostic' I can understand, because implementations can emit those till
the cows come home. But if by 'error' you mean failed to translate the code,
then either you've failed to invoke your C compilers in conforming mode, or
you've only ever used broken C compilers.

[Note: C++ compilers are not C compilers.]
This is hardly conclusive, but strong circumstantial evidence.

You are correct, it's hardly conclusive at all.
And in any events, failing to return a value from a fn you explicitly
declared to return a value is just plain bad programming.

True, but it was quite common in K&R C:

blah() { puts("blah"); }
main() { blah(); }

The original C committee obviously decided not to break such code, although
why they didn't go the whole nine yards with C99, I don't know.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top