EOF是否会设置errno?

5
我总是苦恼于系统调用的返回值 - 它们太不一致了!通常我会检查它们是否为空或为-1,然后调用 perror。但是,对于fgets,手册上说:

gets()fgets() 成功时会返回s,发生错误或在读取任何字符之前出现文件结束符时会返回NULL

这意味着返回值NULL不一定是一个错误 - 它也可能是EOF。当达到文件结尾时,errno是否被设置?在这种情况下,我仍然可以调用perror吗?

如果不行,那么怎样区分函数调用返回错误和EOF是普遍的方法?我想在错误时使用带有空字符串的perror,而在EOF时使用自定义字符串。

2个回答

7
使用ferrorfeof来区分错误和EOF。如果存在错误,没有一般方法可以准确地找出错误是什么,但您可以知道是否存在错误。
标准C的(f)gets(和(f)getc)不需要设置errno,尽管符合规范的库实现可以随意将errno设置为非零值。
Posix要求(f)get{c,s}在读取错误时设置errno,但这只有在通过调用ferror确定存在读取错误后才有帮助。还要记住,库函数永远不会将errno设置为0,但即使没有发生错误,也可能将errno设置为非零值。因此,您不能将测试errno作为检查任何库函数的错误返回值(包括fgets)的替代方法。由于文件结尾不是错误,因此errno在EOF时(可能)不会被修改,因此在该情况下它的值是无意义的。

@Xufeng:不,它以某种实现特定的方式直接从FILE对象获取信息。 - rici
@Xufeng:并不是,因为errno还没有被设置(至少不是由ferror(f)gets设置),所以perror的结果最多只会误导你。你能做的就是打印一些本地化版本的“I/O错误”,但即使errno被设置了,perror也不会更好。 - rici
1
哦,既然你说fgets不会设置errno,我做了一些研究,发现fgets本来就不是一个系统调用.. :) - Xufeng
2
@JanHudec:标准C包含头文件<errno.h>的定义,其中包括对errno的定义:“...它扩展为一个可修改的lvalue,具有int类型和线程本地存储持续时间,其值由多个库函数设置为正错误号。” 其中一个这样的函数是strtod,它也不是系统调用。 - rici
@rici:嗯,你说得对。标准C库有时确实会使用errno,但像FILE*函数这样的其他时候则不会。 - Jan Hudec
显示剩余5条评论

2
根据fputs自身的文档,是的,EOF确实会设置errno。手册间接地暗示了它,而不是直接说明,希望会被修正。函数fputs返回一个整数,成功时为正数,失败时为EOF。所以处理fputs错误的关键是设置一个代码块,在调用fputs时检查返回值。以下是我学习如何处理fputs错误的片段。
if (fputs(buffer, stdout) == EOF)
{
  fprintf(stderr, "fputs returned EOF: %s\n", strerror(errno));
  // .. and now do whatever cleanup you need to do.
  // or be lazy and exit(-1)
}

在这里,我将缓冲区的内容写入标准输出并检查fputs是否返回EOF。EOF表示设置了错误代码,因此只要您遵循fputs的man页面上的文档,您就应该能够创建一堆if语句来检查errno可能设置的各种错误代码。

(1) 什么是缓冲区?一些我在其他地方声明的字符数组。

(2) fprintf是做什么的?它将输出打印到传递的文件描述符中,在这种情况下是标准错误(stderr……它像stdout一样打印到控制台,但用于错误)。

(3) strerror是什么?它是在string.h头文件中定义的函数,用于打印传入的错误代码的错误信息。它对errno可能设置的每个错误代码都有信息。头文件string.h不应与strings.h混淆,后者是一个不包含strerror(3)的BSD linux头文件。

编辑:好吧,我搞错了。您正在寻找关于fgets而不是fputs的答案。

要检查fgets的错误,请执行以下操作

if (fgets(buffer, BUF_SIZE, myFile) == NULL)
{ 
  // print out error as a string to stderr
  fprintf(stderr, "fgets error occurred: %s\n", strerror(errno));
  // do cleanup
}
// special: you also need to check errno AFTER the if statement...

事实上,只有在流变得无法读取时才会出现错误,这可能是由于权限或尝试读取处于写入模式的内容所致。在网络的情况下,可能会有某些东西在读取过程中切断您的连接,在这种情况下,您需要在fgets if语句之后检查错误代码。但如果出现问题,它将设置错误代码。
至少这是如果手册正确的话。有关更多详细信息,请参见Linux手册页。唯一可以设置的错误代码是“我无法读取此内容”的代码,即errno == EBADF。

注意事项:1)OP的帖子是关于fgets()而不是fputs()。2)根据C11标准,fputs()errno不起作用。也许在*nix中会有所不同。 - chux - Reinstate Monica
@chux:C标准并没有说fputs()errno不做任何操作。它也没有说明它是否会执行任何操作。N1570 7.5p3:“库函数调用可以将**errno的值设置为非零,无论是否存在错误,只要在本国际标准的函数描述中未记录使用errno**。” POSIX规定 fputcfputs都可以设置一些errno值。 - Keith Thompson
是的,我在这个问题上有点搞砸了。我在不同的事情之间转换时误读了问题。你是正确的。 - user4586128
@Keith Thompson 是的 - 这比我对errno的评论说得更好。 - chux - Reinstate Monica
通常情况下,如果某些东西切断了网络,底层对read()的调用将会一直阻塞,直到文件系统本身放弃(这可能会导致当前进程进入磁盘休眠,也可能不会)。另外,欢迎来到本站,并感谢您在回答问题时的努力 - 我认为您会因为那种风格而在这里表现出色。 - Tim Post
== NULL - Keith Thompson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接