为什么glibc的fclose(NULL)会导致分段错误而不是返回错误?

时间:2013-06-04 16:23:00

标签: c linux segmentation-fault fclose

根据手册页fclose(3)

  

返回值

     

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

     

错误

     

EBADF fp下的文件描述符无效。

     

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);
}

5 个答案:

答案 0 :(得分:25)

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

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

答案 1 :(得分:6)

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)。您的软件可能会明显崩溃,而不是继续处于不一致的状态。

答案 2 :(得分:4)

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

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

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

  

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

答案 3 :(得分:1)

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

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

  fclose returns 0 if successful (including when FP is NULL or not an open file)

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

答案 4 :(得分:1)

我认为该联机帮助页讨论了基础文件描述符(当您调用open时,fopen系统调用内部获取的文件描述符无效)您传递给fclose文件指针