什么样的错误将“错误”设置为非零?为什么fopen()设置“errno”而fputc()没有?

时间:2013-05-12 13:22:04

标签: c fopen errno fputcsv

什么样的库函数会影响 errno 并将其设置为非零值会出现什么样的错误?在我的下面的程序中,我打算使用if(errno!=0)作为条件来检查我使用的库函数是否正常运行,这就是我找到的(参见下面的代码):

首先,我使用if(errno!=0)来测试文件是否已使用fopen()成功打开。如果我尝试打开一个不存在的文件,那么errno将设置为非零(在我的情况下为2),并通过在每个阶段打印出errno的值来验证它。但是,如果我打开现有文件,则errno的值保持为零,因为fopen()正确打开文件。在这个问题上,if(errno!=0)充当了if(pFile==NULL)的完美替代品,我已经注释掉了。{/ p>

如果文件成功打开,errno仍为0,则控件将移至第一个else块。这是我发现errno行为混淆的地方。在这里,因为我已经在 r(读取)模式下打开文件并尝试使用fputc()写入文件,所以我希望产生的写入错误将errno设置为非-zero就像fopen()设置的那样,当它无法成功打开文件时。但即使在使用errno写入失败之后,fputc()的值仍然为零。 (这可以通过在错误写入后打印errno的值来验证。)

为什么会这样?为什么一个函数fopen()设置errno而其他函数fputc()面临的写错误不影响errno时,会遇到I / O错误?如果是这样,我们如何可靠地使用errno作为错误指标? 我是否使用errno来测试fopen()是否成功运行,而不是" if(pFile == NULL)"不明智?我将非常感谢您对此的分析答案。

#include <stdio.h>
#include <errno.h>

int main ()
{

  FILE * pFile;
  printf("%d\n",errno);

  pFile = fopen("D:\\decrypt.txt","r");
  printf("%d\n",errno); // Prints 0 if fopen() successful,else 2

  //if(pFile==NULL) perror("Error opening file"); 

   if (errno!=0) perror ("Error opening file");

  else
  {
    fputc ('x',pFile);
     printf("%d\n",errno); //errno shows 0 even after write error

     //if (ferror (pFile))
    if (errno!=0)  //Condition evaluates false even if faulty write
      {
      printf ("Error Writing to decrypt.txt\n"); 
        }
    fclose (pFile);

  }
  return 0; 
}

3 个答案:

答案 0 :(得分:9)

文档主要告诉您哪个函数可以设置errno中的哪些值,但是您需要了解一些规则:

  1. 没有库函数将errno设置为零。
  2. 仅在函数指示发生错误时测试errno才有效(并记录该函数以设置errno)。
  3. 第一点意味着如果您想知道,例如,您是否从strtol()收到错误,则必须在调用之前将errno设置为0.

    第二点至关重要;例如,在Solaris上,在通道不是终端的许多I / O操作之后,errno的设置将是ENOTTY(不是终端)。没有错误;一切都没有失败;但基于errno单独执行后续操作(而不是基于I / O操作报告的状态)将导致您认为一切都失败了。

    因此,在您的代码中,fopen()调用可能会将errno作为非零值,即使它成功创建了文件流。你必须使用:

    const char filename[] = "D:\\crypt.txt";
    if ((pFile = fopen(filename, "r")) == 0)
    {
        fprintf(stderr, "Failed to open %s for reading (%d: %s)\n",
                filename, errno, strerror(errno));
        ...return or exit...
    }
    

    注意:如果您需要调用可以改变errno的函数,请尽早捕获该值:

        int errnum = errno;
        fprintf(stderr, "Failed to open %s for reading (%d: %s)\n",
                filename, errnum, strerror(errnum));
    

    永远不要自己宣布errno;始终使用#include <errno.h>来执行此操作。

    我不清楚为什么您的代码在fputc()调用时没有收到错误。在我的Mac OS X 10.8.3系统上,等效代码失败,errno设置为9(EBADF)'错误的文件描述符'。


    这在哪里记录?它符合C标准,并通过POSIX标准加强。

    ISO / IEC 9899:2011§7.5错误<errno.h>

      

    ¶3初始线程中errno的值在程序启动时为零(其他线程中errno的初始值为不确定值),但任何库都不会将其设置为零函数。 202) errno的值可以通过库函数调用设置为非零,无论是否存在错误,前提是errno的使用未记录在本国际标准中对该功能的描述。

         

    202)因此,使用errno进行错误检查的程序应该在库函数调用之前将其设置为零,然后在后续库函数调用之前检查它。当然,库函数可以在进入时保存errno的值,然后将其设置为零,只要在返回之前errno的值仍然为零时恢复原始值。

    以前版本的C标准中的措辞没有提及线程,但在其他方面类似。

    请注意,C标准中fopen()的说明未提及errno。因此,允许按C标准设置errno。相比之下,记录mbsrtowcs()函数将errno设置为EILSEQ;它可能无法将其设置为其他值,因为C标准说它不应该(尽管没有什么可以阻止实现,如果它对某些条件有更好的错误)。

    POSIX 2008

    errno的POSIX页面说:

      

    许多函数在errno中提供错误编号,其类型为int,并在<errno.h>中定义。 errno的值仅在调用明确声明为其设置的函数之后定义,并且直到它被下一个函数调用更改或者应用程序为其赋值。仅当errno被函数的返回值指示为有效时,才应检查errno的值。申请应通过包含<errno.h>来获得errno的定义。此卷POSIX.1-2008中的任何函数都不应将errno设置为0.除非该函数的描述指定{{1},否则未成功调用函数后errno的设置}不得修改。

         

    未指定errno是宏还是使用外部链接声明的标识符。如果为了访问实际对象而禁止宏定义,或者程序定义名称为errno的标识符,则行为未定义。

         

    存储在errno中的符号值记录在所有相关页面的错误部分中。

    以前版本的措辞相似。

答案 1 :(得分:2)

  

我是否使用errno来测试fopen()是否成功运行,而不是“if(pFile == NULL)”不明智?

来自C99标准的 7.5 Errors / 3

  

在程序启动时errno的值为零,但是任何库函数都不会将其设置为零.159)可以通过库函数调用将errno的值设置为非零,无论是否存在错误,前提是在本国际标准的功能描述中没有记录errno的使用。

因此,检查errno以确定操作的成功或失败是不明智的,因为允许函数悲观地设置errno的值以指示失败,即使没有。仅在函数失败时查询errno(例如fopen()返回NULLfputc()返回EOF)。

答案 2 :(得分:1)

只需查看手册页以确认有效的错误编号都是非零的;任何系统调用或库函数都不会将errno设置为零。

检查the Linux errno.h man page: 我相信您应该查看返回值,然后查看errno