When C was invented the main focus was to get as much efficiency as possible without syntax overhead. Therefore in C language you will not find safety mechanics present in higher level languages of today.
Most functions indicate that they detected an error by returning a special value, typically NULL for functions that return pointers, and −1 for functions that return integers. In addition to this you can set "errno" global variable. Any non zero value for "errno" after a function call will indicate an error.
Various system errors are defined in <error.h> header file. You can use these codes to compare errno and do some actions depending on the error code. It is a good practice, to set errno to zero (0) before computation or action. After you can check errno to verify if operation was successful.
Only error code will not be good enough for user to understand the error. Therefore a good practice is to send an error message to consol or to standard error stream. This can be redirected by the user into a log file using a script or command line arguments.
Standard C provide two functions that are useful to produce the error message:
perror() function: displays the string you pass to it, followed by a colon, a space, and then the textual representation of the current errno value.
strerror() function: returns a pointer to the textual representation of the current errno value.
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno; //global variable
int main () {
FILE * f;
//try to open file "test"
f = fopen ("test", "r");
//check if operation was successful
if (f == NULL) {
fprintf(stderr, "Error: %d, %s\n", errno, strerror(errno));
} else {
fclose (f);
}
return 0;
}
Note: It is better to have a preventive design in your application than a reactive design. What that means is you can check parameter values before you use these values as parameters to call other functions or computations to prevent possible errors.
A reactive design uses whatever values and analyze later if the computation was successful. After the fact you may have less information available about the reason the computation was failed. However it is a good practice to check errno every time even if you use preconditions to prevent the errors in the first place.
Read next: Type Casting