fgets() 返回 NULL 是否符合短缓冲区要求?

2024-02-05

在单元测试中,函数包含fgets(),当缓冲区大小时遇到​​意外结果n < 2。显然这样的缓冲区大小是愚蠢的,但测试正在探索极端情况。

简化代码:

#include <error.h>
#include <stdio.h>

void test_fgets(char * restrict s, int n) {
  FILE *stream = stdin;
  s[0] = 42;
  printf("< s:%p n:%d stream:%p\n", s, n, stream);
  char *retval = fgets(s, n, stream);
  printf("> errno:%d feof:%d ferror:%d retval:%p s[0]:%d\n\n",
    errno, feof(stream), ferror(stream), retval, s[0]);
}

int main(void) {
  char s[100];
  test_fgets(s, sizeof s);  // Entered "123\n" and works as expected
  test_fgets(s, 1);         // fgets() --> NULL, feof() --> 0, ferror() --> 0
  test_fgets(s, 0);         // Same as above
  return 0;
}

令人惊讶的是fgets()回报NULL and neither feof() nor ferror() are 1.

下面的 C 规范似乎对这种罕见的情况保持沉默。

问题:

  • 正在返回NULL无需设置feof() nor ferror()合规行为?
  • 不同的结果是否是合规行为?
  • 如果有的话有什么区别吗n是1还是小于1?

平台:gcc 版本 4.5.3 目标:i686-pc-cygwin

以下是 C11 标准的摘要,其中有一些重点:

7.21.7.2fgets功能

The fgets函数读取的字符数最多比指定的字符数少一个n [...]

The fgets函数返回s如果成功的话。如果遇到文件结尾and没有字符被读入数组,数组的内容保持不变并返回空指针。如果操作过程中发生读取错误,则数组内容不确定并返回空指针。

相关帖子
如何对 fgets 使用 feof 和ferror(C 语言中的 minishell) https://stackoverflow.com/questions/21724004/minishell-in-c-how-to-use-feof-and-ferror-for-fgets
在 C 中创建 shell 时遇到问题(Seg-Fault 和ferror) https://stackoverflow.com/questions/12716276/trouble-creating-a-shell-in-c-seg-fault-and-ferror
fputs()、fgets()、ferror() 问题和 C++ 等效项 https://stackoverflow.com/questions/9059486/fputs-fgets-ferror-questions-and-c-equivalents
fgets()的返回值 https://stackoverflow.com/questions/21679063/return-value-of-fgets


[编辑]对答案的评论

@Shafik Yaghmour 很好地提出了总体问题:因为 C 规范没有提到当它不读取时要做什么any数据也不写any数据到s when (n <= 0),这是未定义的行为。因此任何合理的回应都应该是可以接受的,例如退货NULL,不设置标志,保留缓冲区。

至于什么时候应该发生什么n==1,@Oliver Matthews 的回答和 @Matt McNabb 的评论表明,考虑到缓冲区,C 规范缺乏清晰度n == 1。 C 规范seems有利于缓冲n == 1应该返回缓冲区指针s[0] == '\0',但还不够明确。


新版本中的行为有所不同glibc, for n == 1,它返回s这表明成功,这并不是一个无理的解读7.19.7.2 fgets 函数段落2其中说(C99和C11都是一样的,重点是我的):

char *fgets(char * 限制 s,int n, FILE * 限制流);

fgets 函数最多读取比 n 指定的字符数少 1 个的字符从stream指向的流到s指向的数组中。无需额外 在换行符(保留)之后或文件结尾之后读取字符。在读入数组的最后一个字符之后立即写入一个空字符。

不是很有用,但不违反标准中所说的任何内容,它最多会读0字符和空终止符。因此,您看到的结果看起来像是在后续版本中修复的错误glibc。它显然也不是文件结尾,也不是段落中所述的读取错误3:

[...]如果遇到文件结尾并且没有字符读入数组,则数组的内容保持不变并返回空指针。如果操作过程中发生读取错误,则数组内容不确定并返回空指针。

就最后的情况而言n == 0这看起来只是未定义的行为。 C99标准部分草案4. 一致性段落2 says (强调我的):

如果违反了约束之外的“应”或“不应”要求,则行为是未定义的。未定义的行为另有说明在本国际标准中使用“未定义行为”一词或省略任何明确的行为定义。这三者的侧重点没有区别;它们都描述了“未定义的行为”。

C11 中的措辞相同。这是不可能阅读的最多 -1 个字符它既不是文件结束也不是读取错误。所以我们对这种情况下的行为没有明确的定义。看起来像是一个缺陷,但我找不到任何涵盖此问题的缺陷报告。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

fgets() 返回 NULL 是否符合短缓冲区要求? 的相关文章

随机推荐