如何在scanf()前彻底清空stdin缓冲区及其中的换行符\n
首先得明确一个关键点:fflush(stdin)是不符合C标准的行为——C标准只规定fflush对输出流有效,输入流的fflush属于未定义行为,虽然像MinGW、MSVC这类Windows下的编译器会实现它,但这不是通用方案,而且容易出现你遇到的这种清理不彻底的情况。
你之前尝试的rewind(stdin)、fseek(stdin)也不可靠,因为stdin作为交互式终端输入流,本身不支持随机访问,这类操作对它来说同样是未定义行为,在很多环境下根本不起作用。而单独调用一次getchar()只能处理单个残留字符,一旦缓冲区里有多个字符(比如多次回车),就会漏处理,还会在缓冲区为空时导致程序挂起等待输入。
标准且可靠的清空stdin方法
正确的做法是循环读取缓冲区里的所有字符,直到读到换行符(用户输入的结束标志)或者EOF为止。可以封装成一个复用的函数:
void clear_stdin(void) { int c; // 循环读取,直到把缓冲区里的字符全部读完 while ((c = getchar()) != '\n' && c != EOF) { // 不需要做任何操作,只是把字符从缓冲区里"取走" } }
这个函数的逻辑很简单:只要缓冲区里还有没被读取的字符(不是换行也不是EOF),就一直读,直到把所有残留内容清空。不管用户输入了多少次回车、多少额外字符,都能一次性处理干净。
修改你的代码
把原来的fflush(stdin)替换成这个函数,同时注意在每次scanf之后也要清空缓冲区(因为scanf("%c")只会读取一个字符,用户输入的回车符会留在缓冲区里):
#include <stdio.h> #include <unistd.h> // MinGW下sleep函数的头文件 void clear_stdin(void) { int c; while ((c = getchar()) != '\n' && c != EOF) { /* 丢弃读取到的字符 */ } } int AAA () { const char *p = "Enter to proceed.. X to Exit.. : "; const char *ok = "OK" ; char proceed1 ; char proceed2 ; printf(p); clear_stdin(); // 先清空可能存在的残留缓冲区 scanf("%c", &proceed1); if (proceed1 == 'X') { return 0 ; } clear_stdin(); // 清空第一次输入后残留的回车/其他字符 sleep(3); printf("\n%s", p); scanf("%c", &proceed2); if (proceed2 == 'X') { return 0 ; } printf("\n%s", ok); return 0 ; }
额外补充:用scanf格式符简化处理
如果你不想单独写清空函数,也可以修改scanf的格式控制符,在%c前面加一个空格:
scanf(" %c", &proceed1);
这里的空格会让scanf自动跳过所有空白字符(包括换行、空格、制表符),直接读取第一个非空白字符,也能避免残留换行的问题。不过这种方法是让scanf自己处理空白,而不是主动清空整个缓冲区,如果你需要确保缓冲区完全干净(比如后续还有其他输入操作),还是前面的clear_stdin函数更稳妥。
内容的提问来源于stack exchange,提问作者Maurizio

