You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 16:46:03