What kind of errors set “errno” to non-zero? Why does fopen() set “errno” while fputc() does not?

落爺英雄遲暮 提交于 2019-12-04 23:05:43

问题


What kind of errors faced by what kind of library functions affect the errno and set it to non-zero value? In my following program, I intended to use if(errno!=0) as a condition to check if the library functions I used functioned properly or not, and this is what I found (see the code below):

First I used if(errno!=0) to test if a file has been opened successfully with fopen() or not. If I try to open a non-existent file, then errno is set to non-zero (2 in my case) and it is verified by printing out the value of errno at each stage. But if I open an existing file, then the value of errno remains zero as fopen() opens the file properly. In this matter,if(errno!=0) acts as the perfect substitute to if(pFile==NULL) which I have commented out.

If the file is opened successfully, with errno still 0, the control moves to the first else block. This is where I find confusions about the behavior of errno. Here, since I have opened the file in r(read) mode and attempting to write to it using fputc(), I expect the resulting write-error to set errno to non-zero just as it is set by fopen() when it couldn't open the file successfully. But the value of errno continues to be zero even after that unsuccessful write using fputc(). (This is verifiable by printing value of errno after the faulty write).

Why is it so? Why is I/O error faced by one function fopen() setting the errno while write error faced by other function fputc() not affecting errno? If it's so, how can we reliably use errno as an indicator of error? Is my use of errno to test if fopen() worked successfully, instead of "if(pFile==NULL)" unwise? I will appreciate your analytic answers to this.

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

回答1:


The documentation largely tells you which function can set which values in errno, but there are some rules you need to know:

  1. No library function sets errno to zero.
  2. It is only valid to test errno when the function has indicated that an error has occurred (and the function is documented to set errno).

The first point means that if you want to know whether, for example, you've gotten an error from strtol(), you have to set errno to 0 before calling it.

The second point is crucial; for example, on Solaris, after many I/O operations when the channel was not a terminal, the setting of errno would be ENOTTY (not a terminal). There wasn't an error; nothing had failed; but basing your subsequent actions on errno alone (and not on the status reported by the I/O operation) would lead you to think everything failed.

Thus, in your code, the fopen() call may leave errno as a non-zero value, even though it successfully creates the file stream. You have to use:

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...
}

Beware: if you need to call a function that can alter errno, capture the value early:

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

And never declare errno yourself; always use #include <errno.h> to do it.

I'm not clear why your code is not getting an error on the fputc() call. On my Mac OS X 10.8.3 system, the equivalent code fails with errno set to 9 (EBADF) 'Bad file descriptor'.


Where is this documented? It's in the C standard, and reinforced by the POSIX standard.

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

¶3 The value of errno in the initial thread is zero at program startup (the initial value of errno in other threads is an indeterminate value), but is never set to zero by any library function.202) The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in this International Standard.

202) Thus, a program that uses errno for error checking should set it to zero before a library function call, then inspect it before a subsequent library function call. Of course, a library function can save the value of errno on entry and then set it to zero, as long as the original value is restored if errno’s value is still zero just before the return.

The wording in previous versions of the C standard did not mention threads but was otherwise similar.

Note that the description of fopen() in the C standard does not mention errno. Therefore, it is permitted to set errno by the C standard. By contrast, the mbsrtowcs() function is documented to set errno to EILSEQ; it probably can't set it to other values because the C standard says it shouldn't (though there's nothing much to stop an implementation from doing so if it has a better error for some condition).

POSIX 2008

The POSIX page for errno says:

Many functions provide an error number in errno, which has type int and is defined in <errno.h>. The value of errno shall be defined only after a call to a function for which it is explicitly stated to be set and until it is changed by the next function call or if the application assigns it a value. The value of errno should only be examined when it is indicated to be valid by a function's return value. Applications shall obtain the definition of errno by the inclusion of <errno.h>. No function in this volume of POSIX.1-2008 shall set errno to 0. The setting of errno after a successful call to a function is unspecified unless the description of that function specifies that errno shall not be modified.

It is unspecified whether errno is a macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual object, or a program defines an identifier with the name errno, the behavior is undefined.

The symbolic values stored in errno are documented in the ERRORS sections on all relevant pages.

The wording in previous versions was similar.




回答2:


Is my use of errno to test if fopen() worked successfully,instead of "if(pFile==NULL)" unwise?

From 7.5 Errors/3 of the C99 standard:

The value of errno is zero at program startup, but is never set to zero by any library function.159) The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in this International Standard.

So checking errno to determine success or failure of an operation is unwise as a function is permitted to, pessimistically, set the value of errno to indicate failure, even if there is none. Only query errno if a function fails (if fopen() returns NULL or fputc() returns EOF for example).




回答3:


Just looked into the manual pages to confirm that valid error numbers are all nonzero; errno is never set to zero by any system call or library function.

Check the Linux errno.h man page: I believe you should look at return value and then look at errno.



来源:https://stackoverflow.com/questions/16507816/what-kind-of-errors-set-errno-to-non-zero-why-does-fopen-set-errno-while

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!