为何fgets(s, n, stream)在n≤1时并非都返回空字符串而是NULL?
fgets函数在n≤1时的行为差异与标准解析
根据C17标准草案7.21.7.2,
fgets函数声明为:char *fgets(char * restrict s, int n, FILE * restrict stream);标准定义的行为规则:
- 从
stream中读取最多n-1个字符(直至首个'\n'或EOF)存入s数组,最后追加'\0'。- 返回规则:
- 返回
NULL:
- 若调用后立即遇到EOF(
s数组内容不变)- 若发生读取错误(
s数组内容不确定)- 返回
s:其他成功执行的情况
按照上述标准逻辑推导,当n≤1时,fgets最多读取0个字符,追加'\0'后应返回s,不会触发EOF或读取错误判定。但实际在GCC和MSVC下的测试结果与预期不符:
n=1时返回空字符串n≤0时返回NULL
测试代码
#include <stdio.h> #include <string.h> int main(void) { char s[20]; char *cp; int n; for (n = 2; n >= -1; --n) { strcpy(s, "HHHHH"); cp = fgets(s, n, stdin); printf("n == %d:\n", n); printf(" \"%s\"\n", s); if (cp == NULL) printf(" fgets returned NULL\n"); } printf("The end of main has been reached.\n"); return 0; }
测试现象
输入abcde时:
- GCC环境:
n=0、n=-1时s保持"HHHHH"且fgets返回NULL - MSVC环境:
n=-1时程序直接终止
疑问解答
为何n≤1时fgets并非都返回空字符串?
n=1时,fgets会直接在s的起始位置写入'\0',此时返回s,对应空字符串,这部分符合标准逻辑;但n≤0时属于标准未明确的边界情况,不属于合法的参数输入范围,行为不受标准约束。即使n≤0时未读取内容,为何会触发EOF判定?
n≤0时,标准中“读取最多n-1个字符”的逻辑完全不成立,属于未定义行为,编译器可以自行实现处理逻辑。GCC选择直接返回NULL并保留s原有内容,而MSVC则直接终止程序,这些都是未定义行为的典型表现,并非真正触发了EOF判定。
最终结论
n≤0时,fgets的行为属于标准未明确的未定义行为,不同编译器可自行选择处理方式。n=1时逻辑合法,fgets会写入空字符串并返回s,但标准的表述存在模糊性,有优化空间。- 截至C2x草案,该模糊点仍未修正,
n≤0的处理存在四种潜在实现方式,此处暂不讨论各方式的优劣。
内容的提问来源于stack exchange,提问作者Lover of Structure
相关产品推荐
相关产品推荐

