【问题标题】:Why glibc's fclose(NULL) cause segmentation fault instead of returning error?为什么 glibc 的 fclose(NULL) 会导致分段错误而不是返回错误?
【发布时间】:2013-05-31 03:30:50
【问题描述】:

根据手册页fclose(3):

返回值

成功完成后返回 0。否则,返回 EOF 并且 全局变量errno 设置为指示错误。在任何一种情况下,任何进一步 对流的访问(包括对fclose() 的另一次调用)导致 未定义的行为。

错误

EBADFfp 底层的文件描述符无效。

fclose() 函数也可能失败并为任何错误设置errno 为例程 close(2)、write(2) 或 fflush(3) 指定。

当然fclose(NULL) 应该失败,但我希望它正常返回errno,而不是直接因分段错误而死亡。这种行为有什么原因吗?

提前致谢。

更新:我将把我的代码放在这里(我正在尝试strerror(),特别是)。

FILE *not_exist = NULL;

not_exist = fopen("nonexist", "r");
if(not_exist == NULL){
    printError(errno);
}

if(fclose(not_exist) == EOF){
    printError(errno);
}

【问题讨论】:

标签: c linux segmentation-fault fclose


【解决方案1】:

这个fclose() 问题似乎是 FreeBSD 的遗留问题,并且被微软和 Linux 阵营不加批判地接受。

但另一方面,HP、SGI、Solaris 和 CYGWIN 都可以合理地处理fclose(NULL)。例如,man fclose 用于 CYGWIN,它使用 newlib 而不是 OP 的 glibc,声明:

fclose 成功返回 0(包括 FP 为 NULL 或不是打开文件时)

有关相关讨论,请参阅https://stackoverflow.com/a/8442421/318716。

【讨论】:

  • 如果“合理”是“隐藏表现出未定义行为的完全损坏的代码”,那么我想这是合理的。
【解决方案2】:

fclose(NULL)应该成功。 free(NULL) 成功,因为这样可以更轻松地编写清理代码。

很遗憾,它不是这样定义的。因此,您不能在可移植程序中使用fclose(NULL)。 (例如,参见http://pubs.opengroup.org/onlinepubs/9699919799/)。

正如其他人所提到的,如果您将 NULL 传递到错误的位置,您通常不希望返回错误。您需要一条警告消息,至少在调试/测试版本中。取消引用 NULL 会立即给您一条警告消息,并有机会收集标识编程错误的回溯 :)。当你在编程时,段错误是你能得到的最好的错误。 C 有许多更细微的错误,调试时间更长...

可能会滥用错误返回来提高对编程错误的鲁棒性。但是,如果您担心软件崩溃会丢失数据,请注意可能会发生完全相同的情况,例如如果您的硬件断电。这就是我们有自动保存 (since Unix text editors with two-letter names like ex and vi) 的原因。最好让您的软件明显崩溃,而不是继续处于不一致的状态。

【讨论】:

    【解决方案3】:

    手册页所说的错误是运行时错误,而不是编程错误。您不能只将NULL 传递给任何期望指针的API,并期望该API 做一些合理的事情。将 NULL 指针传递给记录为需要指向数据的指针的函数是一个错误。

    相关问题:In either C or C++, should I check pointer parameters against NULL/nullptr?

    引用R.'s comment对该问题的答案之一:

    ...您似乎将操作环境中的异常情况(fs 已满、内存不足、网络关闭等)引起的错误与编程错误混淆了。在前一种情况下,一个健壮的程序当然需要能够优雅地处理它们。在后者中,一个健壮的程序首先无法体验它们。

    【讨论】:

      【解决方案4】:

      我认为手册页讨论的是底层文件描述符(当您调用fopen 时,它在内部通过open 系统调用获得的那个)无效,而不是您传递给fclose的文件指针。

      【讨论】:

        【解决方案5】:

        fclose 需要一个FILE 指针作为其参数,该指针由fopen、标准流之一stdin、stdout 或stderr 或以其他一些实现定义的方式获得。空指针不是其中之一,因此行为是未定义的,就像fclose((FILE *)0xdeadbeef) 一样。 NULL 在 C 中并不特殊;除了保证与任何有效指针比较不等于这一事实之外,它就像任何其他无效指针一样,并且使用它会调用未定义的行为,除非您将接口作为其合同的一部分传递给文档 @987654328 @ 有一些特殊的含义。

        此外,返回错误将是有效的(因为无论如何行为是未定义的)但对实现有害的行为,因为它隐藏了未定义的行为。调用未定义行为的最佳结果始终是崩溃,因为它会突出显示错误并让您能够修复它。 fclose 的大多数用户不检查错误返回值,我敢打赌,大多数愚蠢到将NULL 传递给fclose 的人不会聪明到检查@987654332 的返回值@。人们可能会提出一个论点,即人们应该检查fclose的返回值,因为最终刷新可能会失败,但这对于仅为读取而打开的文件是不必要的,或者如果@ 987654334@ 在 fclose 之前被手动调用(无论如何,这是一个更聪明的习惯用法,因为在您仍然打开文件时更容易处理错误)。

        【讨论】:

        • 它“隐藏”了错误,因为程序继续运行,没有任何迹象表明有问题。这种隐藏是active 而不是by omission,因为代码必须针对 NULL 主动测试指针以避免崩溃。这比根本不执行检查更糟糕,因为它使程序更难调试。如果它立即崩溃(默认行为),您将立即看到错误并修复它。通过检查 null(以及由于断言失败而导致崩溃或中止失败),允许错误状态持续/传播。
        • 有趣的是,malloc/free 没有遵循这种推理,其中释放空指针非常好。不一致...
        • @Thomas:free 接受空指针的要求不是实现选择;它是由语言规定的。有人声称此要求与允许malloc(0) 成功返回空指针这一事实有关,但我还没有看到任何官方确认该声明。
        • 我想问题是为什么 C 标准没有使 fclose(0); 成为无操作,就像 free(0); 一样。当然,这不会破坏任何现有代码或导致任何不良后果。
        • 该语句的简单读法是不正确的:'...空指针不是其中之一...' 这是不正确的。空指针是fopen在文件无法打开时返回的指针。附带说明一下,fclose 接受空指针是合适的,唉,标准不需要它。
        猜你喜欢
        • 2014-04-25
        • 1970-01-01
        • 1970-01-01
        • 2019-12-08
        • 2021-07-18
        • 2020-04-15
        • 1970-01-01
        • 2017-08-18
        相关资源
        最近更新 更多