C语言控制台猜词游戏:getchar()等待回车时如何实时更新剩余时间?
Hey there! Let's fix that real-time countdown issue in your word guessing game. The core problem here is that getchar() is a blocking function—it stops your program dead in its tracks until the user presses a key, so your remaining time can't update until then. Here are two practical ways to handle this:
Approach 1: Use select() to Listen for Input and Time Simultaneously
The select() function lets you monitor file descriptors (like standard input) for activity, with a timeout. This way, you can check for user input every second, and update the countdown if no input is received.
Here's how to modify your code:
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/select.h> #include <unistd.h> #include <time.h> // Assume these are defined in your code: // char *parole[]; // int n; // int indovinate = 0; // int secLeft = 60; int main() { // Initialize random seed for proper word selection srand(time(NULL)); char parola[256]; // Adjust size based on your longest word struct timeval tv; fd_set readfds; while (secLeft > 0) { system("clear"); // Pick a random word snprintf(parola, sizeof(parola), "%s", parole[rand() % n]); // Print game state immediately printf("\n\n[%d]\t\t%s\t\t%d seconds left", indovinate, parola, secLeft); fflush(stdout); // Ensure output isn't buffered // Set up select to wait 1 second for input FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); tv.tv_sec = 1; tv.tv_usec = 0; int result = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); if (result > 0) { // User pressed a key—clear the input buffer until Enter char c; while ((c = getchar()) != '\n' && c != EOF); // Increment correct guess count indovinate++; } else if (result == 0) { // 1 second passed without input—decrement remaining time secLeft--; } else { // Handle select() error perror("select failed"); break; } } // Game over screen system("clear"); printf("\n\nTime's up! You guessed %d words correctly.\n", indovinate); return 0; }
Key Details:
select()checks if input is ready on standard input. If not, it waits the full 1-second timeout before continuing, letting us update the countdown.fflush(stdout)ensures the game state prints right away—without this, buffered output might not show until the program unblocks.- We read all characters until Enter to clear the input buffer, so leftover keystrokes don't mess up the next round.
Approach 2: Use Signals for Periodic Updates
Another option is to use a SIGALRM signal that triggers every second to update the countdown. You'll need to handle interruptions to getchar() caused by the signal.
Here's a working implementation:
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <time.h> int secLeft = 60; int indovinate = 0; char *parole[] = {"apple", "banana", "cherry"}; // Example word list int n = 3; char current_word[256]; // Track current word for signal handler void update_countdown(int sig) { secLeft--; // Refresh game display system("clear"); printf("\n\n[%d]\t\t%s\t\t%d seconds left", indovinate, current_word, secLeft); fflush(stdout); // Re-arm the alarm for the next second alarm(1); } int main() { srand(time(NULL)); struct sigaction sa; // Set up signal handler for SIGALRM sa.sa_handler = update_countdown; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); // Start the periodic alarm alarm(1); while (secLeft > 0) { system("clear"); // Pick and store current word snprintf(current_word, sizeof(current_word), "%s", parole[rand() % n]); printf("\n\n[%d]\t\t%s\t\t%d seconds left", indovinate, current_word, secLeft); fflush(stdout); // Handle input—retry if interrupted by the alarm signal char c; while ((c = getchar()) != '\n' && c != EOF) { if (errno == EINTR) { // Retry the read if it was interrupted by SIGALRM continue; } } if (secLeft > 0) { indovinate++; } } system("clear"); printf("\n\nTime's up! You guessed %d words correctly.\n", indovinate); return 0; }
Key Details:
- The
SIGALRMsignal fires every second, triggeringupdate_countdown()to decrement time and refresh the display. - When
getchar()is interrupted by the signal, it returns-1and setserrnotoEINTR—we check for this and retry the input read. - We store the current word in a global variable so the signal handler can display it consistently.
Either approach will let you update the remaining time in real-time while waiting for the user to press Enter. The select() method is generally more portable and avoids signal-handling pitfalls, making it a solid first choice.
内容的提问来源于stack exchange,提问作者Tom Riddle

