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

为何fgets()会将字符串末尾的换行符识别为单独一行?

关于fgets()读取换行符的行为疑问

我正在为大学操作系统课程编写一个简单的C语言Shell程序,用while循环配合fgets()读取输入命令并逐行解析。输入格式都是"some_string\n",我会把读取到的内容存入缓冲区后移除末尾的换行符,代码如下:

#define MAX_LINE_SIZE 4096
char* buff = (char*) malloc(MAX_LINE_SIZE * sizeof(char));

while(fgets(buff, MAX_LINE_SIZE, stdin) != NULL) {
    *strchr(buff, '\n') = '\0';

    // 若无此检查,当单独读取最后一个'\n'字符时,程序后续会触发段错误
    if(strlen(buff) == 0) {
        break;
    }
    
    // 解析并执行命令
}

free(buff);

在测试如下输入时,我发现最后一个字符串末尾的换行符被当作单独一行读取,生成了长度为0的字符串(如果没有上面的if检查会触发段错误):

debug\n
debug 1\n
debug\n
debug off\n
debug\n

具体表现是:最后一个debug被当作不含\n的单独行读取,而其后的\n则在下次循环迭代中被单独读取。我对此感到困惑,因为根据手册说明,fgets()应该读取直到(包含)\n或(不包含)EOF的整行字符串,存储后返回该字符串。


问题原因及修正方案

首先要指出,你代码里的*strchr(buff, '\n') = '\0';存在严重隐患:当fgets()没有读取到\n时(比如输入的最后一行没有换行符就触发了EOF,或者输入行长度超过缓冲区上限),strchr()会返回NULL,此时解引用空指针直接导致段错误——这才是你遇到崩溃的核心原因,而非空行本身。

至于你描述的“最后一个debug和\n被分开读取”的情况,正常用户输入场景下不会出现(输入debug加回车会作为完整的一行被输入流推送,fgets()会一次性读取debug\n)。只有当输入流被拆分成两次传递时才会触发:比如先向输入流写入debug,程序的fgets()读取到内容后返回(因为没遇到\n且输入流未结束),之后再向输入流写入\n,此时fgets()会读取到单独的\n,处理后变成空字符串。

正确的换行符处理方式应该先检查缓冲区末尾是否存在\n,避免空指针解引用:

#define MAX_LINE_SIZE 4096
char* buff = (char*) malloc(MAX_LINE_SIZE * sizeof(char));

while(fgets(buff, MAX_LINE_SIZE, stdin) != NULL) {
    size_t len = strlen(buff);
    // 仅当缓冲区最后一个字符是'\n'时才移除它
    if (len > 0 && buff[len - 1] == '\n') {
        buff[len - 1] = '\0';
    }
    
    // 空行处理,这里用continue跳过而非break,根据你的需求调整
    if(strlen(buff) == 0) {
        continue;
    }
    
    // 解析并执行命令
}

free(buff);

这种方式通过strlen()获取长度后检查最后一位,比strchr()更安全,能覆盖所有fgets()的读取场景。

内容的提问来源于stack exchange,提问作者quaintly_crowish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 19:04:53