C Programming – Should Every Little Error Be Checked?

cerror handling

As a good programmer one should write robust codes that will handle every single outcome of his program. However, almost all functions from the C library will return 0 or -1 or NULL when there's an error.

It's sometimes obvious that error checking is needed, for example when you try to open a file. But I often ignore error checking in functions such as printf or even malloc because I don't feel necessary.

if(fprintf(stderr, "%s", errMsg) < 0){
    perror("An error occurred while displaying the previous error.");
    exit(1);
}

Is it a good practice to just ignore certain errors, or is there a better way to handle all the errors?

Best Answer

In general, code should deal with exceptional conditions wherever it is appropriate. Yes, this is a vague statement.

In higher level languages with software exception handling this is often stated as "catch the exception in the method where you can actually do something about it." If a file error occurred, maybe you let it bubble up the stack to the UI code that can actually tell the user "your file failed to save to disk." The exception mechanism effectively swallows up "every little error" and implicitly handles it at the appropriate place.

In C, you do not have that luxury. There are a few ways to handle errors, some of which are language/library features, some of which are coding practices.

Is it a good practice to just ignore certain errors, or is there a better way to handle all the errors?

Ignore certain errors? Maybe. For example, it is reasonable to assume that writing to standard output will not fail. If it does fail, how would you tell the user, anyway? Yes, it is a good idea to ignore certain errors, or code defensively to prevent them. For example, check for zero before dividing.

There are ways to handle all, or at least most, errors:

  1. You can use jumps, similar to gotos, for error handling. While a contentious issue among software professionals, there are valid uses for them especially in embedded and performance-critical code (e.g. Linux kernel).
  1. Cascading ifs:

    if (!<something>) {
      printf("oh no 1!");
      return;
    }
    if (!<something else>) {
      printf("oh no 2!");
      return;
    }
    
  2. Test the first condition, e.g. opening or creating a file, then assume subsequent operations succeed.

Robust code is good, and one should check for and handle errors. Which method is best for your code depends on what the code does, how critical a failure is, etc. and only you can truly answer that. However, these methods are battle-tested and used in various open source projects where you can take a look to see how real code checks for errors.

Related Topic