scanf(), ungetc() behaviour.

A

Argento

I was curious at the start about how ungetc() returns the character to the
stream, so i did the following coding. Things work as expected except if I
change the scanf("%c",&j) to scanf("%d",&j). I don't understand how could
scanf() affect the content of i[0] and i[1]. Can someone tell me why?

#include <stdio.h>
#include <ctype.h>

void main()
{
char i[2], j;
printf("Please input a two digit number:");
i[0]= getchar();
i[1]= getchar();
ungetc(i[1],stdin);
ungetc(i[0],stdin);
scanf("%c", &j); //scanf("%d",&j);
fflush(stdin);
printf("\nYou entered %d in decimal and %c in character mode for
i[0]",i[0],i[0]);
printf("\nYou entered %d in decimal and %c in character mode for
i[1]",i[1],i[1]);
printf("\nYou entered %d in decimal and %c in character mode for j",j,j);

getchar();

}
 
J

Jack Klein

I was curious at the start about how ungetc() returns the character to the
stream, so i did the following coding. Things work as expected except if I
change the scanf("%c",&j) to scanf("%d",&j). I don't understand how could
scanf() affect the content of i[0] and i[1]. Can someone tell me why?

Your program has so many instances of undefined behavior that there is
nothing at all that the C standard has to say about what it should do.
#include <stdio.h>
#include <ctype.h>

void main()

The C standard requires that main() be defined with a return type of
int in a hosted environment, unless you have a C99 conforming compiler
that specifically documents that it accepts "void main()", and I will
most certainly bet that you do not. This means your program is
undefined.
{
char i[2], j;
printf("Please input a two digit number:");
i[0]= getchar();
i[1]= getchar();
ungetc(i[1],stdin);
ungetc(i[0],stdin);

You haven't checked the return value of the ungetc() function,
particularly the second one. Only one character of push back is
guaranteed by the standard. The second one could have failed.
scanf("%c", &j); //scanf("%d",&j);

'j' is a character and has a size of one byte (with 8 or more bits).
If you pass scanf() a "%d" conversion specifier and the address of
'j', you are telling scanf() to write an int into that one byte. On
your platform int has more than one byte, and so won't fit into 'j'
which only has room for a single character. You are lying to scanf()
when you use "%d" and this causes undefined behavior.
fflush(stdin);

The fflush() function is not defined for input streams, more undefined
behavior.
printf("\nYou entered %d in decimal and %c in character mode for
i[0]",i[0],i[0]);
printf("\nYou entered %d in decimal and %c in character mode for
i[1]",i[1],i[1]);
printf("\nYou entered %d in decimal and %c in character mode for j",j,j);

getchar();

}

Fix the undefined behavior in your program and then ask questions if
you are puzzled about what it does.
 
A

Argento

The C standard requires that main() be defined with a return type of
int in a hosted environment, unless you have a C99 conforming compiler
that specifically documents that it accepts "void main()", and I will
most certainly bet that you do not. This means your program is
undefined.

Well, sorry. I don't know much about the standard but i am using DEV C++ and
the compiler compiles it happily.
I am just lazy with the 'return' when i don't check for it.

{
char i[2], j;
printf("Please input a two digit number:");
i[0]= getchar();
i[1]= getchar();
ungetc(i[1],stdin);
ungetc(i[0],stdin);

You haven't checked the return value of the ungetc() function,
particularly the second one. Only one character of push back is
guaranteed by the standard. The second one could have failed.

Ok, i checked on this after your post by replacing the two ungetc statement
with the following.
if( (ungetc(i[1],stdin))== i[1] ) printf("First ungetc() success.\n");
if( (ungetc(i[0],stdin))== i[0] ) printf("Second ungetc() success.\n");

It seems like that, i have no problem with pushing back both characters.


'j' is a character and has a size of one byte (with 8 or more bits).
If you pass scanf() a "%d" conversion specifier and the address of
'j', you are telling scanf() to write an int into that one byte. On
your platform int has more than one byte, and so won't fit into 'j'
which only has room for a single character. You are lying to scanf()
when you use "%d" and this causes undefined behavior.

You are right, I was careless. If I change j to 'int' things are solve,
however, it's still weird to change the content of i[0] and i[1].
Maybe scanf('%d", &j) when j is 'char' overwrite the memory of i[0]?
The fflush() function is not defined for input streams, more undefined
behavior.

Well, i just keep on seeing examples that uses fflush( stdin ) and i was
told to do so after a scanf() and before a getchar()
because the '\n' is not taken by scanf("%d") or scanf("%c"). That was my
practice of clearing the stdin's buffer.
Any suggestion/alternative of doing so would be appreciated.
 
M

Martin Ambuhl

Argento said:
I was curious at the start about how ungetc() returns the character to the
stream, so i did the following coding. Things work as expected except if I
change the scanf("%c",&j) to scanf("%d",&j). I don't understand how could
scanf() affect the content of i[0] and i[1]. Can someone tell me why?

#include <stdio.h>
#include <ctype.h>

void main()
^^^^
Don't bother with trying to learn how ungetc() works until you stop this
absurd BullSchildtism.
 
R

Richard Heathfield

Argento said:
Well, sorry. I don't know much about the standard but i am using DEV C++
and the compiler compiles it happily.

Nevertheless, it is incorrect. And now you *know* it's incorrect.

I know for certain that at least one regular contributor to this group, and
possibly more than one, has got into the habit of completely ignoring any
"plea-for-help" article in which the questioner has used void main, on the
grounds that they can't be all that serious about C if they can't even get
the entry point right.
 
A

Argento

Martin Ambuhl said:
Argento said:
I was curious at the start about how ungetc() returns the character to
the stream, so i did the following coding. Things work as expected
except if I change the scanf("%c",&j) to scanf("%d",&j). I don't
understand how could scanf() affect the content of i[0] and i[1]. Can
someone tell me why?

#include <stdio.h>
#include <ctype.h>

void main()
^^^^
Don't bother with trying to learn how ungetc() works until you stop this
absurd BullSchildtism.

Ok, so i listened and search, then read about this
http://www.eskimo.com/~scs/readings/voidmain.960823.html
But i don't quite understand whether i should end my program with exit(0) or
return 0;
Can you explain to me pls?
 
A

Argento

Nevertheless, it is incorrect. And now you *know* it's incorrect.

I know for certain that at least one regular contributor to this group,
and
possibly more than one, has got into the habit of completely ignoring any
"plea-for-help" article in which the questioner has used void main, on the
grounds that they can't be all that serious about C if they can't even get
the entry point right.
I see. I didn't know it was that serious until i looked into the faq.
I was taught that way, "when you don't check for the return of main, why
declare int main?"
Now i know after reading
http://www.eskimo.com/~scs/readings/voidmain.960823.html
and i will change that habit :p
 
K

Keith Thompson

Argento said:
Ok, so i listened and search, then read about this
http://www.eskimo.com/~scs/readings/voidmain.960823.html
But i don't quite understand whether i should end my program with exit(0) or
return 0;
Can you explain to me pls?

"exit(0);" and "return 0;" (within main) are almost exactly
equivalent. If you use exit, you must have a "#include <stdlib.h>" at
the top of your source file. The exit function can be used to
terminate the program from functions other than main; a return
statement from a function other than main just returns from that
function.

Use whichever one you like. (I usually use return, but it's no a big
deal.)
 
N

Nick Keighley

Argento wrote:

{
char i[2], j;
printf("Please input a two digit number:");

if you want your output to be guaranteed to appear put a
\n at the end or call fflush(stdout)

i[0]= getchar();
i[1]= getchar();
ungetc(i[1],stdin);
ungetc(i[0],stdin);

is there some sort of international shortage of whitespace
no one told me about? This is the third post in three days
which uses an atrocious layout. An indent of one!
You haven't checked the return value of the ungetc() function,
particularly the second one. Only one character of push back is
guaranteed by the standard. The second one could have failed.

Ok, i checked on this after your post by replacing the two ungetc statement
with the following.
if( (ungetc(i[1],stdin))== i[1] ) printf("First ungetc() success.\n");
if( (ungetc(i[0],stdin))== i[0] ) printf("Second ungetc() success.\n");

It seems like that, i have no problem with pushing back both characters.

it's not portable though

Well, i just keep on seeing examples that uses fflush( stdin )

they are wrong. If your book uses void main() and fflush(stdout) then
destroy it. DO NOT give it to a charity shop as some other poor sucker
will mistake it for a C book. If you want a good book try
http://cm.bell-labs.com/cm/cs/cbook/
and i was
told to do so after a scanf() and before a getchar()
because the '\n' is not taken by scanf("%d") or scanf("%c"). That was my
practice of clearing the stdin's buffer.
Any suggestion/alternative of doing so would be appreciated.

don't use scanf() use fgets() followed by sscanf()


--
Nick Keighley

"Half-assed programming was a time-filler that, like knitting,
must date to the beginning of human experience."
"A Fire Upon The Deep" by Verne Vinge
 
A

Argento

"exit(0);" and "return 0;" (within main) are almost exactly
equivalent. If you use exit, you must have a "#include <stdlib.h>" at
the top of your source file. The exit function can be used to
terminate the program from functions other than main; a return
statement from a function other than main just returns from that
function.

Use whichever one you like. (I usually use return, but it's no a big
deal.)

Thx, that's what i got too from K&R2 Sec. 7.6 pp.164. Except that it says
exit has the advantage call from other function and that exit calls flcose
for each
open output file to flush out any buffered output.

Yet, a friend of mine told me not to rely on it and do the flcose() myself.
 
A

Argento

if you want your output to be guaranteed to appear put a
\n at the end or call fflush(stdout)

Thx for the clue.
i[0]= getchar();
i[1]= getchar();
ungetc(i[1],stdin);
ungetc(i[0],stdin);

is there some sort of international shortage of whitespace
no one told me about? This is the third post in three days
which uses an atrocious layout. An indent of one!

English is not my native language, i don't quite understand what you
mean.
Are you saying that my coding habit is lack of whitespace? If it is, well, i
was
just trying to test how ungetc works , so it's like draft work...sorry.
You haven't checked the return value of the ungetc() function,
particularly the second one. Only one character of push back is
guaranteed by the standard. The second one could have failed.

Ok, i checked on this after your post by replacing the two ungetc
statement
with the following.
if( (ungetc(i[1],stdin))== i[1] ) printf("First ungetc() success.\n");
if( (ungetc(i[0],stdin))== i[0] ) printf("Second ungetc() success.\n");

It seems like that, i have no problem with pushing back both characters.

it's not portable though

Not portable? Why not?
they are wrong. If your book uses void main() and fflush(stdout) then
destroy it. DO NOT give it to a charity shop as some other poor sucker
will mistake it for a C book. If you want a good book try
http://cm.bell-labs.com/cm/cs/cbook/

No, no , no. My book doesn't use void main() it was my lazy habit (which i
will change from now onwards)
It was fflush( stdin ) that i use, not fflush( stdout ) :p
As a matter of fact, i do have K&R's book but i only made it to page 38.
don't use scanf() use fgets() followed by sscanf()

I do know about this, what i meant was alternative to fflush( stdin ). I am
a beginner who doesn't
have much experience with stream, I/O and memory, so i always run away from
using fgets()
and sscanf() in cases like this one (just to test a function). I will put
more hard work on learning
how to use these functions so that i won't make the same mistakes again.
Thank you.
 
A

Argento

fflush(stdin);
they are wrong. If your book uses void main() and fflush(stdout) then
destroy it. DO NOT give it to a charity shop as some other poor sucker
will mistake it for a C book. If you want a good book try
http://cm.bell-labs.com/cm/cs/cbook/


don't use scanf() use fgets() followed by sscanf()

Ah, i read about this about a week ago.
http://c-faq.com/stdio/gets_flush2.html

while((c = getchar()) != '\n' && c != EOF)
/* discard the character */;

FYI, unfortunately, when i check back on my university's course unit just
now. They do use
void main() and fflush( stdin ) :(
 
J

John Tsiombikas (Nuclear / Mindlapse)

Thx, that's what i got too from K&R2 Sec. 7.6 pp.164. Except that it says
exit has the advantage call from other function and that exit calls flcose
for each
open output file to flush out any buffered output.

No, both a return from main, and a call to exit results in closing all
the open files and flushing the repsective buffers. They are identical
in this respect.
Yet, a friend of mine told me not to rely on it and do the flcose() myself.

That's what I do, but not out of fear that returning from main will let
them open. It's a matter of style, it simply looks better, more
symmetric if you will, to explicitly close what you open.
 
J

John Tsiombikas (Nuclear / Mindlapse)

English is not my native language, i don't quite understand what you
mean. Are you saying that my coding habit is lack of whitespace? If
it is, well, i was just trying to test how ungetc works , so it's like
draft work...sorry.

I've heard this excuse a lot, "it's just a test program, no need to
indent properly", "I'm not going to show it to anyone", etc.
However, fact is that in order to be able to build a good programming
style, which you follow automatically and write readable code even in
your sleep, you *have* to use proper indentation and consistent style
even in 5-line tests. That way, writting readably becomes a habbit and
you don't even have to think about it anymore.
if( (ungetc(i[1],stdin))== i[1] ) printf("First ungetc() success.\n");
if( (ungetc(i[0],stdin))== i[0] ) printf("Second ungetc() success.\n");

It seems like that, i have no problem with pushing back both characters.

it's not portable though

Not portable? Why not?

Because the C standard says: "One character of pushback is guaranteed.
If the ungetc function is called too many times on the same stream
without an intervening read or file positioning operation on that
stream, the operation may fail."
 
M

Mark McIntyre

Not portable? Why not?

Because the C standard doesn't require it. Therefore if you tried this
again on ac different version of linux, or on windows, or on VMS, it
might not work. Its very dangerous to rely on undefined behaviour,
which just happens to work ok on your particular platform, unless you
understand *precisely* why it works.

This is actually a FAQ - search for fflush.

Mark McIntyre
 
M

Mark McIntyre

FYI, unfortunately, when i check back on my university's course unit just
now. They do use
void main() and fflush( stdin ) :(

Yes, its very annoying when that happens. All one can do is ignore it,
and learn the correct way.
Mark McIntyre
 
K

Keith Thompson

Argento said:
Thx, that's what i got too from K&R2 Sec. 7.6 pp.164. Except that it
says exit has the advantage call from other function and that exit
calls flcose for each open output file to flush out any buffered
output.

Yes, exit has the advantage that you can call it from anywhere, but
all open files will be closed whether you use exit or return. (The
only difference is an extremely subtle one involving accessing objects
local to main from a function registered with atexit(); you'll almost
certainly never run into anything like that.)
Yet, a friend of mine told me not to rely on it and do the flcose()
myself.

Yes, that's a good idea -- not because the implementation might not
close the files (it's required to by the standard), but because it's
just good practice. Your main program could some day become part of a
larger program, for example. It's (almost) always a good idea to
clean up after yourself.
 
A

Arndt Jonasson

Argento said:
I see. I didn't know it was that serious until i looked into the faq.
I was taught that way, "when you don't check for the return of main, why
declare int main?"

When you don't check for the return of main, why spend one more
letter declaring it ("void" instead of "int")?
(Not that this would be any sort of working argument if "int" was spelled
"integer"...)
 
A

Arndt Jonasson

Argento said:
Martin Ambuhl said:
Argento said:
I was curious at the start about how ungetc() returns the character to
the stream, so i did the following coding. Things work as expected
except if I change the scanf("%c",&j) to scanf("%d",&j). I don't
understand how could scanf() affect the content of i[0] and i[1]. Can
someone tell me why?

#include <stdio.h>
#include <ctype.h>

void main()
^^^^
Don't bother with trying to learn how ungetc() works until you stop this
absurd BullSchildtism.

Ok, so i listened and search, then read about this
http://www.eskimo.com/~scs/readings/voidmain.960823.html
But i don't quite understand whether i should end my program with exit(0) or
return 0;
Can you explain to me pls?

I prefer using 'return' from main, for about the only reason that I
may otherwise get compilation warnings telling me that the code
between the call to 'exit' and the end of the function is not reached.

There is a preprocessor symbol defined by the standard for indicating
successful exit: EXIT_SUCCESS (and one for failure - EXIT_FAILURE). It
is usually 0, but I suppose it must be something else in some environment.
 
D

Default User

Argento wrote:

'j' is a character and has a size of one byte (with 8 or more bits).
If you pass scanf() a "%d" conversion specifier and the address of
'j', you are telling scanf() to write an int into that one byte. On
your platform int has more than one byte, and so won't fit into 'j'
which only has room for a single character. You are lying to
scanf() when you use "%d" and this causes undefined behavior.

You are right, I was careless. If I change j to 'int' things are
solve, however, it's still weird to change the content of i[0] and
i[1]. Maybe scanf('%d", &j) when j is 'char' overwrite the memory of
i[0]?


It's not weird at all. As Jack told you, it's trying to write more data
than will fit into a char. So where does the extra data go? In your
case, it writes into some other automatic data "adjacent" to the char
variable j. This is one form of undefined behavior, silently changing
some other variables. If you're lucky, the program crashes when you do
something like that. You didn't get that lucky.



Brian
 

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

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top