C语言循环读取非阻塞IO字符串行为异常的问题排查
字符串终止命令无法被程序识别的IO问题
问题场景
- 程序包含while循环,循环内执行操作,通过非阻塞IO(select)接收终止进程的字符串命令
- 用户可随时输入命令并回车终止进程
- select检测到输入后,将输入字符串与"EXIT"对比,匹配则结束进程
问题现象
使用字符串变量作为终止键时,程序无法识别"EXIT"命令,持续输出循环内容,必须按Ctrl+C强制终止。尝试过scanf()、fgets()、fread()读取输入,也用fflush(stdin)和fflush(stdout)清理缓冲区,问题依旧。但使用单个char作为终止键时无此异常。
附问题代码
字符串版本(存在问题)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int main() { fd_set s; struct timeval timeout; timeout.tv_sec = 3; timeout.tv_usec = 0; char exitFlag[10]; int sFlag; printf("Enter \"EXIT\" to End the Process\n"); while (1) { fflush(stdin); fflush(stdout); FD_ZERO(&s); FD_SET(STDIN_FILENO, &s); timeout.tv_sec = 3; timeout.tv_usec = 0; sFlag = select(STDIN_FILENO+1, &s, NULL, NULL, &timeout); printf("%d: ", sFlag); if (sFlag < 0) _exit(0); else if (sFlag == 0) { printf("-\n"); fflush(stdin); fflush(stdout); } else { fflush(stdin); fflush(stdout); scanf("%[^\n]s", &exitFlag); // fgets( exitFlag, 10, stdin); // fread( exitFlag, 10, 1 ,stdin); printf("You have Pressed %s, Enter \"EXIT\" to End the Process: ", exitFlag); printf("%s\n", exitFlag); if ( memcmp(&exitFlag, "EXIT", 4) == 0 ) break; } } return 0; }
单个字符版本(正常工作)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int main() { fd_set s; struct timeval timeout; timeout.tv_sec = 3; timeout.tv_usec = 0; char exitFlag; int sFlag; while (1) { fflush(stdout); FD_ZERO(&s); FD_SET(STDIN_FILENO, &s); timeout.tv_sec = 3; timeout.tv_usec = 0; sFlag = select(STDIN_FILENO+1, &s, NULL, NULL, &timeout); if (sFlag < 0) _exit(0); else if (sFlag == 0) { fflush(stdout); printf("-\n"); } else { fflush(stdin); scanf("%c", &exitFlag); printf("You have Pressed %c, Press E to exit: ", exitFlag); printf("%c\n", exitFlag); fflush(stdout); if ( exitFlag =='E' ) break; } } return 0; }
问题根源与修复方案
1. scanf格式符错误
%[^\n]是完整的格式说明符,用于读取直到换行符的所有字符,后面多余的s会导致scanf匹配失败,无法正确读取输入内容。此外,exitFlag是数组名,本身就是指针,不需要加&,直接传入exitFlag即可。
2. 缓冲区残留换行符
用户输入"EXIT"并回车时,%[^\n]会读取"EXIT",但换行符\n会留在stdin缓冲区中。下一次select检测到缓冲区有数据,会立即触发读取,但读取到的是换行符,导致exitFlag内容异常,无法匹配"EXIT"。
3. memcmp用法错误
memcmp(&exitFlag, "EXIT", 4)中,exitFlag是数组名,不需要加&,正确写法是memcmp(exitFlag, "EXIT", 4)。但更推荐用strcmp,因为它会比较到字符串结束符,避免出现输入内容长度超过4时的部分匹配问题。
4. fflush(stdin)的未定义行为
C标准中,fflush仅对输出流有定义,对输入流的行为是未定义的,不要依赖它清理输入缓冲区,这只会导致跨平台兼容性问题。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int main() { fd_set s; struct timeval timeout; char exitFlag[10]; int sFlag; printf("Enter \"EXIT\" to End the Process\n"); while (1) { fflush(stdout); FD_ZERO(&s); FD_SET(STDIN_FILENO, &s); timeout.tv_sec = 3; timeout.tv_usec = 0; sFlag = select(STDIN_FILENO+1, &s, NULL, NULL, &timeout); printf("%d: ", sFlag); if (sFlag < 0) { perror("select error"); _exit(1); } else if (sFlag == 0) { printf("-\n"); fflush(stdout); } else { // 限制读取长度为9,避免缓冲区溢出,去掉多余的s if (scanf("%9[^\n]", exitFlag) == 1) { printf("You have entered: %s\n", exitFlag); // 使用strcmp进行完整字符串匹配 if (strcmp(exitFlag, "EXIT") == 0) { break; } } // 清除缓冲区中残留的换行符及多余字符 while (getchar() != '\n'); fflush(stdout); } } printf("Process exited successfully\n"); return 0; }
关键修复说明
- 修正
scanf格式为%9[^\n],限制读取长度防止缓冲区溢出,移除多余的s - 使用
strcmp替代memcmp,确保完整字符串匹配 - 读取完成后通过
while (getchar() != '\n')手动清除缓冲区中的换行符,避免下一次select误触发 - 移除无效的
fflush(stdin),改用标准方式清理输入缓冲区 - 增加
select错误处理,打印错误信息便于调试
内容的提问来源于stack exchange,提问作者Msain
相关产品推荐
相关产品推荐

