flo*_*pus 5 c stdio language-lawyer
在标准 C 中,您如何可靠地检查写入标准 I/O 流的所有输出是否已成功保存到磁盘?
C 标准规定fclose成功时将返回 0,如果“检测到任何错误” ,则返回 EOF。
但这是否意味着“在fclose通话过程中检测到任何错误”?或者这是否意味着“自上次调用clearerr”以来检测到任何错误?
换句话说,程序只检查 的返回值就足够了fclose,还是还需要检查ferror?是否有任何实现,如果ferror返回非零,则后续调用fclose可能返回 0?
这同样适用于fflush:如果fflush返回 0,后续调用也ferror将返回 0,如果fflush返回 EOF,后续调用ferror将返回非零值是否总是这样?是否有任何实现不是这种情况?
(当然,我不考虑停电、小鬼等。是的,需要保证耐用性的程序应该使用fsync,但这超出了标准 C 的范围。)
在没有更好的答案的情况下:
\n\n标准告诉我ferror()返回“错误指示器”的状态,该状态被定义为流状态的一部分。
标准告诉我很多关于何时设置“错误指示器”的信息,但没有透露何时可以清除它 - 除了定义clearerr()和来清除它。 rewind()
ferror()标准没有告诉我,如果在调用函数时设置了“错误指示符”,则任何函数(除了 )预计会做什么。
您的问题似乎基于这样一种可能性:一旦出现错误,就会设置“错误指示器”,并且只有在明确清除时才会清除(它是“锁定的”)。在这种情况下:
\n\nferror()fopen()会告诉您自(或最近的clearerr()或)以来,某些 stdio 函数中发生了错误rewind()。
我不认为标准要求这样做,但也没有说可以不这样做。
fclose()(a) 如果关闭时发生错误,或者(b) 如果已设置“错误指示符”,则可能会返回错误。
而且,如果是这样,成功将意味着自(或最近的或)fclose()以来一切都很好。fopen()clearerr()rewind()
我不认为标准要求这样做,但也没有说可以不这样做。
如果标准没有明确要求某些内容,也没有明确排除某些内容,那么我们就拥有了一只既不活着也不死的猫。
\n\n简而言之,我认为该标准无法以某种方式回答您的任何问题。
\n\n该标准的保守解读是:
\n\n在每次 stdio 函数调用后立即检查错误,并进行相应处理。
一般来说,在读取或写入错误之后,放弃并关闭流是明显的响应。
\n\nfclose()可能会(再次)返回当前错误,或者返回一个新错误,或者根本不返回错误。如果要返回错误,我将返回原始的读/写错误。在要报告错误的地方,我将报告原始读/写错误和fclose().
如果决定继续输入/输出,“错误指示器”可能(或可能不会)影响进一步的功能,并且可能(或可能不会)被大多数进一步的功能清除......
\n\n...所以最好是clearerr(),以避免任何可能的混淆。
\n\n然而,我几乎确信fgetc()必须:
A。如果已经设置了“错误指示器”,则立即放弃(并errno再次设置)
或者:
\n\n b. fgetc()清除“错误指示器”——如果没有,那么成功并返回意味着什么EOF?
其他 get 和 put char 和 Wide-char 也是如此。对于所有其他函数来说,情况并非如此,它们的返回值是明确的。
不要期望ferror()告诉您任何有关在最近的 stdio 函数调用之前可能发生的错误的信息。
很明显,这对于错误返回与or返回ferror()相同的少数函数很有用。(如果可能出现具有值或 的字符,则很有用!)。EOFWEOFfeof()EOFWEOF
目前尚不清楚ferror()“错误指示器”是否还有其他用途。
FWIW:我在标准中发现的内容
\n\n标准说 (\xc2\xa77.21.7.1) 对于fgetc():
\n\n\n\n
\n\n- 如果未设置由流指向的输入流的文件结束指示符并且存在下一个字符,则该
\nfgetc函数将获取该字符作为转换为 int 的无符号字符,并推进该流的关联文件位置指示符(如果已定义)。退货
\n\n\n
\n\n- 如果设置了流的文件结束指示符,或者如果流位于文件结束处,则设置流的文件结束指示符并且函数
\nfgetc返回EOF。否则,该fgetc函数返回stream指向的输入流中的下一个字符。如果发生读取错误,则设置流的错误指示符并且函数fgetc返回EOF293)。293)
\nfeof使用和函数可以区分文件结束和读取错误ferror。
我注意到,如果在调用函数时设置了“文件结束指示符”,那么它应该做什么,这是非常清楚的。相反,它并没有以某种方式说明fgetc()如果已经设置了“错误指示器”应该做什么:
应该fgetc()立即失败?
如果是这样,它是否应该设置errno为与首次设置“错误指示器”时相同的值?
然而,如果“错误指示符”之前是由(例如)设置的EINTR,那么注意到它就没有意义。
否则:
\n\n如果此调用成功,应该fgetc()清除“错误指示器”吗?
如果不是,则“错误指示器”可以被视为“锁定”状态,指示自上次清除以来的某个时间发生了错误。
\n\n同样,如果“错误指示器”之前由(例如)设置EINTR,则保留它设置是没有意义的。请注意,C 对EINTR...一无所知,因此实现可以自由地使用“错误指示器”执行不同的操作,具体取决于它的设置方式。
并且,如果fgetc()恰好获取具有值的字符EOF或恰好命中实际的 EOF,那么不清除“错误指示器”将是一个错误!
标准说 (\xc2\xa77.21.7.3) 对于fputc():
\n\n\n\n
\n\n- 该
\nfputc函数将指定的字符c(转换为无符号字符)写入输出流...退货
\n\n\n
\n- 该
\nfputc函数返回写入的字符。如果发生写入错误,则设置流的错误指示符并fputc返回EOF。
再说一次,这并没有指定fputc()如果已经设置了“错误指示器”应该做什么。
这同样适用于fgetwc()和fputwc()。
所有其他输入/输出函数都被定义为“就像”重复fgetc()、fputc()和fgetwc()fputwc()` 一样工作。
标准说 (\xc2\xa77.21.10.3) 对于ferror():
\n\n\n\n
\n\n- 该
\nferror函数测试stream指向的流的错误指示器。退货
\n\n\n
\n- \n
ferror当且仅当为流设置了错误指示符时,该函数才返回非零。
就是这样。上面的脚注293ferror()是我们关于如何使用“错误指示器”的最具体指南。
fflush()(\xc2\xa77.21.5.2)、fseek()(\xc2\xa77.21.9.2) 和fsetpos()(\xc2\xa77.21.9.3) 均定义为在发生错误时设置“错误指示器” 。
rewind()(\xc2\xa77.21.9.5) 和clearerr()(\xc2\xa77.21.10.1) 被定义为清除“错误指示器”。
我没有发现其他对“错误指示器”的引用。
\n