Program entering the default case of switch statement always

P

Phoe6

The Program Fragment is this:

int choice; /* Users Input Command */
..
..
..
printf("Enter a command: ");
fflush(stdin);
choice = getc(stdin);
printf("\n");
..
..
..
while(choice!='Q')
{
switch(choice)
{
/* Movement options */
case 'n':
dx = 0;
dy = -1;
action = 1;
break;
case 's':
..
..
..
..
..
default:
printf("You cannot do that!\n");
action = 0;
break;

}

..
..
..
printf("Enter a Command :");
fflush(stdin);
choice = getc(stdin);
printf("\n");

} /* <- End Brace of while loop */


The Problem I am observing is the Default Section of the Switch command
is being entering all the times (except for the first time).

I am guessing that the choice variable is getting someother value which
is causing it to enter the default; but using
getchar,getc(stdin),fgetc(stdin) and flushing the stdin is not solving
the problem.

---
The Complete Source (with the bug) is here:
http://geocities.com/uthcode2/wumpus.c
'Hunt the Wumpus' Game.
You can see the problem, when compiled and executed(on Linux,gcc)


Regards,
Senthil
 
P

Phoe6

The smallest example which I can give to express the the problem I am
facing is here:

#include<stdio.h>

int main(void)
{
int choice;

choice=getc(stdin);

while(choice='Q')
{
switch(choice)
{
case 'n':
putchar('n');
break;
case 's':
putchar('s');
break;
case 'N':
putchar('N');
break;
case 'S':
putchar('S');
break;
default:
putchar('%');
break;
}
choice=getc(stdin);
}
putchar('Q');

return 0;

}

The Output is always %%; which means that it is entering the default
section no matter what.
 
W

Walter Roberson

:The Problem I am observing is the Default Section of the Switch command
:is being entering all the times (except for the first time).

:The Complete Source (with the bug) is here:
:http://geocities.com/uthcode2/wumpus.c
:'Hunt the Wumpus' Game.
:You can see the problem, when compiled and executed(on Linux,gcc)

The problem does not occur when I compile on IRIX with SGI's
compiler or with gcc.

I do find a bug in the code: shooting south should have
a dy of +1 instead of -1. And of course, having debug set to 1
makes for a pretty uninteresting game.
 
W

Walter Roberson

: The smallest example which I can give to express the the problem I am
:facing is here:

: while(choice='Q')

That's an assignment statement, so you are clobbering choice with
the value 'Q' and then testing whether that is non-zero (which
of course it is). 'Q' is not one of the cases listed so it is going
to use the default tag.

The code you pointed to had while(choice!='Q') which tests
choice's value instead of setting it.
 
P

Phoe6

The problem does not occur when I compile on IRIX with SGI's
compiler or with gcc.

You mean you don not see:

Enter a Command :
You cannot do that!

In the output neccessarily! Thats strange and interesting. Thats what
my whole problem is about
I do find a bug in the code: shooting south should have
a dy of +1 instead of -1.

Thanks, I have corrected it.
 
I

infobahn

Phoe6 said:
The Program Fragment is this:

int choice; /* Users Input Command */
.
.
.
printf("Enter a command: ");
fflush(stdin);

Undefined behaviour. fflush's functionality is defined only for
streams open for output or update.
choice = getc(stdin);

Program may block for input before displaying prompt.
printf("\n");
.
.
.
while(choice!='Q')
{
switch(choice)
{
/* Movement options */
case 'n':
dx = 0;
dy = -1;
action = 1;
break;
case 's':
.
.
.
.
.

This is where you forgot your break statement...

....which is why you fall through to the default case.
 
P

Phoe6

Thanks a lot.
I corrected it to while(choice!='Q')

but still the output is:
[machine@26Feb]# ./a.out
n
n%S
S%S
S%N
N%S
S%N
N%

The '%' things which are coming up.(which is the original bug)

-Senthil
 
K

Kiru Sengal

Phoe6 said:
The Program Fragment is this:

int choice; /* Users Input Command */
.
.
.
printf("Enter a command: ");
fflush(stdin);

You can't portably flush stdin, even if you could there would be no
point in doing it at this location. This location is a perfect place
to fflush(stdout), because you don't have a newline in your printf
format string.
choice = getc(stdin);

After getc returns a character, there would still be AT LEAST a newline
sitting in stdin (and possible some other junk if the user didn't
immediately hit ENTER after providing a character). This isn't true if
the user hits ENTER right from the start, in which case there won't be
anything sitting in stdin, so you must be careful about what I just
said.

printf("\n");
while(choice!='Q')
{
switch(choice)
{
/* Movement options */
case 'n':
dx = 0;
dy = -1;
action = 1;
break; [snipped some cases]
default:
printf("You cannot do that!\n");
action = 0;
break;

}

.
.
.
printf("Enter a Command :");
fflush(stdin);
choice = getc(stdin);
printf("\n");
You are repeating yourself down here. To get around this, use a
do-while loop, which always executes the body at least once (and only
tests the condition at the bottom)
} /* <- End Brace of while loop */


The Problem I am observing is the Default Section of the Switch command
is being entering all the times (except for the first time).

Yes, because there are characters left in stdin.
I am guessing that the choice variable is getting someother value which
is causing it to enter the default; but using
getchar,getc(stdin),fgetc(stdin) and flushing the stdin is not solving
the problem.

Do not fflush stdin.
Do fflush stdout if you don't provide a trailing newline to printf.


I altered your program a little to get it to work. Beware though, this
may not be exactly what you wanted and there are probably more robust &
elegant solutions (I myself rarely take single character inputs, and
have my own wrapper functions to deal with different situations -
leading whitespace, etc). I only provide this to help you understand
what the original bug was:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int choice;

do{
printf("Enter a Command : ");
fflush(stdout);
choice = getc(stdin);

/* clearing stdin manually */
if(choice != '\n')
while(getchar() != '\n')
; /* empty statement */

switch(choice)
{
case 'N':
case 'n': puts("\nYou hit 'n'\n");
break;

case 'S':
case 's': puts("\nYou hit 's'\n");
break;

/* more cases */

case '\n': puts("\nYou didn't provide a choice\n");
break;

case 'Q':
case 'q': puts("\nHit ENTER to quit\n");
getchar();
break;

default: puts("\nYou cannot do that!\n");
break;
}
}while(choice != 'Q' && choice != 'q');

return EXIT_SUCCESS;
}
 
P

Phoe6

Thanks for your reply:

/* clearing stdin manually */
if(choice != '\n')
while(getchar() != '\n')
; /* empty statement */

Solves the problem.
I am unable to understand how this works. :-(
I am getting confused here.

Before posting, I tried with getchar,getc,fgetc etc... hoping to
eliminate the extra '\n' character at input, but failed. How is the
above statement clearing stdin manually and is there anyother way the
problem could have been solved.

Thanks!
Senthil
 
E

Eric Sosman

Phoe6 said:
The smallest example which I can give to express the the problem I am
facing is here:

#include<stdio.h>

int main(void)
{
int choice;
choice=getc(stdin);
while(choice='Q')

ITYM `while (choice != 'Q')' ...
^^

As things stand, `while (choice = 'Q')' sets the variable
to the code for the character 'Q', overwriting whatever
was read from stdin. When you reach the `switch' statement
inside the loop, `choice' is 'Q' no matter what you typed.
 
K

Kiru Sengal

Phoe6 said:
Thanks for your reply:

/* clearing stdin manually */
if(choice != '\n')
while(getchar() != '\n')
; /* empty statement */

Solves the problem.
I am unable to understand how this works. :-(
I am getting confused here.

Before posting, I tried with getchar,getc,fgetc etc... hoping to
eliminate the extra '\n' character at input, but failed. How is the
above statement clearing stdin manually and is there anyother way the
problem could have been solved.


The text input stream (associated with stdin) is "line buffered" so
characters don't appear in stdin, lines do. A line happens to be a
sequence of characters terminated with a newline character.

So when you use getchar, getc(stdin), or fgetc(stdin) you're
essentially telling it to "look in stdin and grab the first available
character" - this is how those functions/macros work. Stdin stays
empty until a full line is sent to it (user hits ENTER to send newline
character and signal end of line).
So getchar, getc, fgetc wait until a character appears. Say the user
hit 'n' and then enter. The character 'n' isn't sent to stdin
immediately, but rather both 'n' and '\n' go to stdin together (because
\n signals end of line, and it's lines that are sent to stdin).

So now stdin has two characters sitting in it (STDIN: 'n' '\n') So now
your getchar, getc, fgetc can grab the n, and return it to your program
(which continues through the switch and back through to getchar, getc,
fgetc again).
This time, getchar, getc, fgetc don't need to wait for the user to
provide anything because stdin is not empty, so they immediately grab
the '\n' (without involving the user in any way) and return it the
program. The third time through the loop, stdin is once again empty,
so the user will have a chance to type something and hit ENTER to
provide stdin with another line.

Now let me ask you a question, what happens if the user types:
snsnsnsnsn[enter] ? [Answer: 11 characters will rush into stdin as a
whole line and your while loop will execute 10 extra times (without
ever needing user input)]

How do you prevent this? I personally wouldn't do character input, but
as a solution to your original code I added the "rest of line consuming
while loop" to clear out the rest of stdin. But, there is a special
case: if the user actually hit ENTER immediately, then stdin would only
have one character in it '\n', which getchar/fgetc/getc grabs and the
consuming while loop would eat the next line coming into stdin (which
we wouldn't want) - thus the "consuming while loop" is only let loose
if it gets passed the IF statement (which verifies that the character
grabbed by getchar, getc, fgetc isn't a newline).

I might be oversimplifying here with terminology/explanation, but I
think I'm making an accurate enough point for your to wrap your mind
around. I really hoped you would only take my original post as a start
to some self-learning/research, and now I hope the same for this post
(you still have to research why fflush(stdin) is a no-no and
fflush(stdout) is required at times).

Take care.
 
P

Phoe6

Thanks a lot, Kiru, for your explaination.
It was really helpful to me and I am getting some details; I will take
your suggestion to go further with this issue to learn more.


Regards,
Senthil
 
G

Gordon Burditt

The Program Fragment is this:
int choice; /* Users Input Command */
.
.
.
printf("Enter a command: ");
fflush(stdin);

fflush(stdin) invokes the wrath of undefined behavior.
choice = getc(stdin);
printf("\n");
.
.
.
while(choice!='Q')
{

Note: newline ('\n') is a real character. Believe it. Worship
it. You type it after entering your command, right? So don't be
too surprised when choice == '\n' some of the time when you run
your program (like maybe every other time around the loop).
switch(choice)
{
/* Movement options */
case 'n':
dx = 0;
dy = -1;
action = 1;
break;
case 's':
.
.
.
.
.
Note: case 's' falls through to the default: case as it is written
here.
default:
printf("You cannot do that!\n");
action = 0;
break;

}

.
.
.
printf("Enter a Command :");
fflush(stdin);
choice = getc(stdin);
printf("\n");

} /* <- End Brace of while loop */


The Problem I am observing is the Default Section of the Switch command
is being entering all the times (except for the first time).

Newline is a real character. You typed it, right?
I am guessing that the choice variable is getting someother value which
is causing it to enter the default; but using
getchar,getc(stdin),fgetc(stdin) and flushing the stdin is not solving
the problem.

fflush(stdin) only creates problems; it does not solve them.

Gordon L. Burditt
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top