admin管理员组文章数量:1406177
I am having a problem with my program getting stuck on waiting for input after using getchar(). I used a loop to clear the buffer and the same functionality appears four times in my program but only on two instances of this while loop it gets stuck and I cannot understand why.
In the two working instances, the only difference is the missing else if
statement but when I remove it from the non-working instances, the problem is not solved and they are still stuck on waiting for a second ENTER.
If I enter anything else than 'y' or 'Y', I am getting the 'invalid input' message and then I am asked for an input again so this functionality works. However, when I enter 'y' or 'Y', followed by ENTER, a new line appears and nothing happens. If I enter invalid input again, I am getting the 'invalid input' message and with correct input, I still get a new line and nothing happens. Only after a second ENTER after a correct input the code continues as it should.
A non-working instance:
printf("Play a game? (Y/N): ");
while (!is_valid) {
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') break;
else {
printf("Invalid input. Try again.\n");
}
}
A working instance:
printf("Roll dice ('R')\n");
while (!is_valid) {
first_roll = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (first_roll == 'r' || first_roll == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
Full code:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h> // For rand()
/*
2025-02-19
RULES
Come-Out Roll:
You roll two dice.
- If the sum is 7 or 11, you win right away.
- If the sum is 2, 3, or 12 (called “craps”), you lose immediately.
- If the sum is 4, 5, 6, 8, 9, or 10, that number becomes your point.
Point Phase:
Once a point is set, you keep rolling the dice.
- If you roll the point number again before rolling a 7, you win.
- If you roll a 7 before the point, you lose.
*/
int roll_dice(void);
bool play_game(void);
int main() {
int roll = 0;
char user_choice;
bool is_valid = false;
// Game loop
printf("Play a game? (Y/N): ");
while (!is_valid) { // In this loop it waits for a second ENTER
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') break;
else {
printf("Invalid input. Try again.\n");
}
}
return 0;
}
int roll_dice(void) {
// Generate 2 random numbers 1-6
int dices = (rand() % 12 + 1);
// Sum the numbers
return dices;
}
bool play_game(void) {
bool result = false;
bool is_valid = false;
bool game_has_ended = false;
char first_roll;
char user_choice;
int roll_result;
getchar();
// One dice roll
printf("Roll dice ('R')\n");
while (!is_valid) { // This loop goes fine
first_roll = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (first_roll == 'r' || first_roll == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
roll_result = roll_dice();
int roll = 1;
int point = 0;
// Evaluate roll
if (roll == 1)
{
switch (roll_result) {
// Immediate win
case 7: {
result = true;
point = 0;
break;
};
case 11: {
result = true;
point = 0;
break;
};
// Immediate lose
case 2: {
result = false;
break;
};
case 3: {
result = false;
break;
};
case 12: {
result = false;
break;
};
// Point
case 4: {
roll = 2;
point = 4;
result = true;
break;
};
case 5: {
roll = 2;
point = 5;
result = true;
break;
};
case 6: {
roll = 2;
point = 6;
result = true;
break;
};
case 8: {
roll = 2;
point = 8;
result = true;
break;
};
case 9: {
roll = 2;
point = 9;
result = true;
break;
};
case 10: {
roll = 2;
point = 10;
result = true;
break;
};
}
}
// Roll 1 results
if (result == false) {
printf("Roll 1: %d\n", roll_result);
printf("You lose!\n");
game_has_ended = true;
}
else if (result == true && roll == 1 && point == 0) {
printf("Roll 1: %d\n", roll_result);
printf("You win!\n");
game_has_ended = true;
}
else {
// Roll 2
if (roll == 2 && point > 0) {
printf("Point: %d\n", roll_result);
char roll_2;
bool is_valid = false;
while (!is_valid) { // This loop goes fine
printf("Press 'R' to roll again.\n");
roll_2 = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {}
if (roll_2 == 'r' || roll_2 == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
// Roll 2 results
switch (roll_dice()) {
case 7: {
result = false;
printf("Roll 2: %d\n", roll_dice());
printf("You lose!\n");
game_has_ended = true;
break;
}
default: {
result = true;
printf("Roll 2: %d\n", roll_dice());
printf("You win!\n");
game_has_ended = true;
break;
}
}
}
}
// Recursion
is_valid = false;
printf("Play another game? (Y/N): ");
while (!is_valid) { // In this loop it waits for a second ENTER
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {}
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') return game_has_ended;
else {
printf("Invalid input. Try again.\n");
}
}
return game_has_ended;
}
Can you give me a hint of what I am doing wrong?
I am having a problem with my program getting stuck on waiting for input after using getchar(). I used a loop to clear the buffer and the same functionality appears four times in my program but only on two instances of this while loop it gets stuck and I cannot understand why.
In the two working instances, the only difference is the missing else if
statement but when I remove it from the non-working instances, the problem is not solved and they are still stuck on waiting for a second ENTER.
If I enter anything else than 'y' or 'Y', I am getting the 'invalid input' message and then I am asked for an input again so this functionality works. However, when I enter 'y' or 'Y', followed by ENTER, a new line appears and nothing happens. If I enter invalid input again, I am getting the 'invalid input' message and with correct input, I still get a new line and nothing happens. Only after a second ENTER after a correct input the code continues as it should.
A non-working instance:
printf("Play a game? (Y/N): ");
while (!is_valid) {
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') break;
else {
printf("Invalid input. Try again.\n");
}
}
A working instance:
printf("Roll dice ('R')\n");
while (!is_valid) {
first_roll = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (first_roll == 'r' || first_roll == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
Full code:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h> // For rand()
/*
2025-02-19
RULES
Come-Out Roll:
You roll two dice.
- If the sum is 7 or 11, you win right away.
- If the sum is 2, 3, or 12 (called “craps”), you lose immediately.
- If the sum is 4, 5, 6, 8, 9, or 10, that number becomes your point.
Point Phase:
Once a point is set, you keep rolling the dice.
- If you roll the point number again before rolling a 7, you win.
- If you roll a 7 before the point, you lose.
*/
int roll_dice(void);
bool play_game(void);
int main() {
int roll = 0;
char user_choice;
bool is_valid = false;
// Game loop
printf("Play a game? (Y/N): ");
while (!is_valid) { // In this loop it waits for a second ENTER
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') break;
else {
printf("Invalid input. Try again.\n");
}
}
return 0;
}
int roll_dice(void) {
// Generate 2 random numbers 1-6
int dices = (rand() % 12 + 1);
// Sum the numbers
return dices;
}
bool play_game(void) {
bool result = false;
bool is_valid = false;
bool game_has_ended = false;
char first_roll;
char user_choice;
int roll_result;
getchar();
// One dice roll
printf("Roll dice ('R')\n");
while (!is_valid) { // This loop goes fine
first_roll = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {} // clear the buffer
if (first_roll == 'r' || first_roll == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
roll_result = roll_dice();
int roll = 1;
int point = 0;
// Evaluate roll
if (roll == 1)
{
switch (roll_result) {
// Immediate win
case 7: {
result = true;
point = 0;
break;
};
case 11: {
result = true;
point = 0;
break;
};
// Immediate lose
case 2: {
result = false;
break;
};
case 3: {
result = false;
break;
};
case 12: {
result = false;
break;
};
// Point
case 4: {
roll = 2;
point = 4;
result = true;
break;
};
case 5: {
roll = 2;
point = 5;
result = true;
break;
};
case 6: {
roll = 2;
point = 6;
result = true;
break;
};
case 8: {
roll = 2;
point = 8;
result = true;
break;
};
case 9: {
roll = 2;
point = 9;
result = true;
break;
};
case 10: {
roll = 2;
point = 10;
result = true;
break;
};
}
}
// Roll 1 results
if (result == false) {
printf("Roll 1: %d\n", roll_result);
printf("You lose!\n");
game_has_ended = true;
}
else if (result == true && roll == 1 && point == 0) {
printf("Roll 1: %d\n", roll_result);
printf("You win!\n");
game_has_ended = true;
}
else {
// Roll 2
if (roll == 2 && point > 0) {
printf("Point: %d\n", roll_result);
char roll_2;
bool is_valid = false;
while (!is_valid) { // This loop goes fine
printf("Press 'R' to roll again.\n");
roll_2 = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {}
if (roll_2 == 'r' || roll_2 == 'R') {
is_valid = true;
}
else {
printf("Invalid input. Try again.\n");
}
}
// Roll 2 results
switch (roll_dice()) {
case 7: {
result = false;
printf("Roll 2: %d\n", roll_dice());
printf("You lose!\n");
game_has_ended = true;
break;
}
default: {
result = true;
printf("Roll 2: %d\n", roll_dice());
printf("You win!\n");
game_has_ended = true;
break;
}
}
}
}
// Recursion
is_valid = false;
printf("Play another game? (Y/N): ");
while (!is_valid) { // In this loop it waits for a second ENTER
user_choice = getchar();
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {}
if (user_choice == 'y' || user_choice == 'Y') {
is_valid = true;
play_game();
}
else if (user_choice == 'n' || user_choice == 'N') return game_has_ended;
else {
printf("Invalid input. Try again.\n");
}
}
return game_has_ended;
}
Can you give me a hint of what I am doing wrong?
Share Improve this question edited Mar 12 at 12:16 Hexe Decima asked Mar 6 at 13:49 Hexe DecimaHexe Decima 456 bronze badges 24- 3 Can't reproduce. Please update with a minimal reproducible example which includes sample input/output. – dbush Commented Mar 6 at 13:56
- 1 @Hexe Decima Does the function play_game() call itself recursively? – Vlad from Moscow Commented Mar 6 at 13:57
- 1 @Hexe Decima By the way the variable ch should have type int. – Vlad from Moscow Commented Mar 6 at 14:07
- 1 @Hexe Decima What is this statement getchar(); in the beginning of the function play_game() doing? – Vlad from Moscow Commented Mar 6 at 15:10
- 3 @HexeDecima Adding an answer to the text of your question is not the correct way to do this. The question was just reopened after my vote. Now you can add an answer to your own question. In addition to your textual description of the problem you should copy&paste the output and input, formatted as a code block, to make it more clear. – Bodo Commented Mar 10 at 9:46
2 Answers
Reset to default 1The function getchar
returns a value of type int
. You should not store the return value of that function in a char
, because depending on the platform you are using,
- the data type
char
may not be able to represent the valueEOF
, and - you may not be able to distinguish the value
EOF
from the value of a valid character code.
It is only safe to store the return value of getchar
into a char
after you have determined that getchar
did not return the value EOF
.
See the following question for further information:
Why must the variable used to hold getchar's return value be declared as int?
To fix this problem, all you have to do is change the data type of ch
from char
to int
.
Since your input is y\n
, the y
is getting consumed by the line
user_choice = getchar();
in the function main
, and the \n
newline character is getting consumed by the following line:
while ((ch = getchar()) != '\n' && ch != EOF) {}
In the function play_game
, you will then call getchar()
again, without first printing a prompt asking the user for further input. This means that the user will have to enter an additional line of input without seeing any input prompt, which will probably be very confusing for the user. This call to getchar()
does not make sense, especially since that function call is discarding the character that it reads.
In order to prevent such problems, I suggest that you do not call the input functions from the C standard library directly. I rather suggest that you create your own function which always reads an entire line of input from the user. This function should also prompt the user for the input, and automatically reprompt the user for input if the previous input is invalid for some reason. This will make the code which handles the main logic of your program much simpler and cleaner, as it does not have to deal with the details of handling user input.
I have created a function get_char_from_user
for this purpose. This function uses my function get_line_from_user
from this answer of mine to another question. I have modified your program to use these functions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
int roll_dice( const char *prompt );
void play_game( void );
char get_char_from_user( const char *prompt );
void get_line_from_user( const char *prompt, char *buffer, int buffer_size );
int main( void )
{
char *prompt = "Play a game? (Y/N): ";
// Game loop
for (;;) // infinite loop
{
char user_choice = get_char_from_user( prompt );
if ( user_choice == 'y' || user_choice == 'Y' )
{
play_game();
prompt = "Play another game? (Y/N): ";
continue;
}
if (user_choice == 'n' || user_choice == 'N')
{
break;
}
printf("Invalid input. Try again.\n");
}
}
int roll_dice( const char *prompt )
{
// repeat until input is valid
for (;;)
{
char roll = get_char_from_user(prompt);
if ( roll == 'r' || roll == 'R' )
{
break;
}
printf("Invalid input. Try again.\n");
}
// NOTE: The comments below seem incorrect, and were taken
// over from the original code
// Generate 2 random numbers 1-6
int dices = (rand() % 12 + 1);
// Sum the numbers
return dices;
}
void play_game( void )
{
bool result = false;
bool game_has_ended = false;
int roll_result;
// One dice roll
roll_result = roll_dice( "Roll dice with 'R': " );
int roll = 1;
int point = 0;
// Evaluate roll
if (roll == 1)
{
switch (roll_result) {
// Immediate win
case 7: {
result = true;
point = 0;
break;
};
case 11: {
result = true;
point = 0;
break;
};
// Immediate lose
case 2: {
result = false;
break;
};
case 3: {
result = false;
break;
};
case 12: {
result = false;
break;
};
// Point
case 4: {
roll = 2;
point = 4;
result = true;
break;
};
case 5: {
roll = 2;
point = 5;
result = true;
break;
};
case 6: {
roll = 2;
point = 6;
result = true;
break;
};
case 8: {
roll = 2;
point = 8;
result = true;
break;
};
case 9: {
roll = 2;
point = 9;
result = true;
break;
};
case 10: {
roll = 2;
point = 10;
result = true;
break;
};
}
}
// Roll 1 results
if (result == false) {
printf("Roll 1: %d\n", roll_result);
printf("You lose!\n");
game_has_ended = true;
}
else if (result == true && roll == 1 && point == 0) {
printf("Roll 1: %d\n", roll_result);
printf("You win!\n");
game_has_ended = true;
}
else
{
// Roll 2
if (roll == 2 && point > 0)
{
printf("Points: %d\n", roll_result);
roll_result = roll_dice( "Enter 'R' to roll again: " );
// Roll 2 results
switch ( roll_result )
{
case 7: {
result = false;
printf("Roll 2: %d\n", roll_result);
printf("You lose!\n");
game_has_ended = true;
break;
}
default: {
result = true;
printf("Roll 2: %d\n", roll_result);
printf("You win!\n");
game_has_ended = true;
break;
}
}
}
}
}
// This function will attempt to read one "char" from the user. This
// "char" must be by itself on its own line. If the input is invalid,
// it will automatically reprompt the user, until the input is valid.
char get_char_from_user( const char *prompt )
{
// loop forever until user enters a single "char"
for (;;)
{
char buffer[1024];
// get one line of input from the user as a string
get_line_from_user( prompt, buffer, sizeof buffer );
// verify that line is not empty
if ( buffer[0] == '\0' )
{
printf( "Error: Line should not be empty.\n" );
continue;
}
// verify that line contains not more than one character
if ( buffer[1] != '\0' )
{
printf( "Error: Please enter only a single character!\n" );
continue;
}
return buffer[0];
}
}
// This function will read exactly one line of input from the
// user. It will remove the newline character, if it exists. If
// the line is too long to fit in the buffer, then the function
// will automatically reprompt the user for input. On failure,
// the function will never return, but will print an error
// message and call "exit" instead.
void get_line_from_user( const char *prompt, char *buffer, int buffer_size )
{
for (;;) //infinite loop, equivalent to while(1)
{
char *p;
// prompt user for input
fputs( prompt, stdout );
// explicitly flushing the output stream
// may be necessary on some platforms
fflush( stdout );
// attempt to read one line of input
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
// attempt to find newline character
p = strchr( buffer, '\n' );
// make sure that entire line was read in (i.e. that
// the buffer was not too small to store the entire line)
if ( p == NULL )
{
int c;
// a missing newline character is ok if the next
// character is a newline character or if we have
// reached end-of-file (for example if the input is
// being piped from a file or if the user enters
// end-of-file in the terminal itself)
if ( (c=getchar()) != '\n' && !feof(stdin) )
{
if ( c == EOF )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
printf( "Input was too long to fit in buffer!\n" );
// discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
// this error message will be printed if either
// a stream error or an unexpected end-of-file
// is encountered
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
// reprompt user for input by restarting loop
continue;
}
}
else
{
// remove newline character by overwriting it with
// null character
*p = '\0';
}
// input was ok, so break out of loop
break;
}
}
This program has the following behavior:
Play a game? (Y/N): sdfhksdh
Error: Please enter only a single character!
Play a game? (Y/N):
Error: Line should not be empty.
Play a game? (Y/N): q
Invalid input. Try again.
Play a game? (Y/N): y
Roll dice with 'R': sdfhksdh
Error: Please enter only a single character!
Roll dice with 'R':
Error: Line should not be empty.
Roll dice with 'R': q
Invalid input. Try again.
Roll dice with 'R': R
Points: 8
Enter 'R' to roll again: R
Roll 2: 11
You win!
Play another game? (Y/N): y
Roll dice with 'R': R
Points: 10
Enter 'R' to roll again: R
Roll 2: 8
You win!
Play another game? (Y/N): n
I have also simplified your program by removing the recursion and a significant amount of code duplication releated to this recursion. Using recursion is generally not a good idea in these kinds of cases, because if the user for example plays 1000 games, then the function play_game
will be in memory 1000 times (unless tail-call optimization is performed by the compiler), which means that all variables of that function will be in memory 1000 times, which will be a big waste of memory. I have therefore modified your program to use iteration instead of recursion.
Also, it appears to make sense to move the code which prompts the user to enter R
to roll the dice into the function roll_dice
, which I have done too. This will also remove some code duplication.
I have also made some other changes, which may or may not be desirable to you, depending on what you exactly want to accomplish.
The problem is solved - thanks to everyone for the hints! The issue was a getchar(); statement in the beginning of the play_game(); function, which I had put in attempts to clear the buffer but without enough knowledge how to do so correctly.
本文标签: cgetchar() keeps waiting after inputcontinues only after pressing ENTER againStack Overflow
版权声明:本文标题:c - getchar() keeps waiting after input - continues only after pressing ENTER again - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744972146a2635298.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论