Always ask yourself the following question:
If I test and there is an error, can I handle it in a meaningful way?
If the answer is "No", then why would you test for it?
E.g. lets take Hello World:
#include <stdio.h>
int main() {
printf("Hello, World!");
return 0;
}
We all know, that printf()
may fail. What are you going to do when it fails? Print an error? Do you think you will be able to print an error in cases you were not even able to print "Hello World!"? Okay, you might be able to print an error to stderr
as just because you cannot print to stdout
doesn't mean you cannot print to stderr
but how meaningful would that be? What would the error say? "I couldn't print Hello World! to standard out"? How is that helpful? What do you expect the user of your program to do with that information? Which reaction do you expect from the user? Shall the user fix standard out and call your program again? Maybe he can, more likely he doesn't even know what standard out is when he calls programs that only serve the purpose to print "Hello World!".
Assume you would not catch that error, what will happen? Nothing. Well, the program will not print "Hello World!" but that will be obvious to the user, right? The user will see that no Hello World appeared on the screen, right? You don't have to tell the user that.
Of course, if the only task of your program is to print "Hello World!" and it cannot do that, you may argue that your program has failed. And there is a meaningful, standard way to communicate to the caller, that a program has failed:
#include <stdio.h>
int main() {
int printRes = printf("Hello, World!");
return (printRes < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
Yes, that is somehow meaningful, you can do that.
But what will you do when malloc(12)
fails? Print an error? If malloc(12)
failed because the system is out of memory, do you think printf()
will work? Can printf()
work without using malloc()
? Well, the answer is, you don't know it and it probably depends what you print but it might as well depend on a memory allocation and that will fail as well. Honestly, what meaningful error handling can you perform if your system is so much out of memory that you cannot even allocate another 12 bytes?
So what would you do? Just call exit(EXIT_FAILURE)
? Is that better than actually crashing because you are accessing the NULL
result of malloc(12)
? Yes, crashing is not nice but at least you will know where the program crashed doing what exactly if a crashlog is written, you know nothing about it if you had just exited. If you had exited, nobody will ever know what went wrong, nobody can ever fix that problem.
What if malloc(100 * 1024 * 1024)
fails? Can you still do error handling? Can you tell the user, that the system is out of memory? Well, most likely you can. You won't need another 100 MB of memory to do that and most likely the system is not completely out of memory, it just cannot provide another 100 MB. At least you should give it a try.
So you see, despite the fact that both examples used malloc()
, it does not depend on the function that failed, it depends on the question whether you can handle that error in a meaningful way.
What shall you do if you cannot handle an error in a meaningful way? Well, this depends on another question:
What is your app doing?
If your app deals with data, you probably don't want data to be lost. So if there is data in flight, which may still be saved and saving is still likely to succeed, you should at least try to save it before you exit. But wait! If you need to ensure data in flight is saved in case of an error, you just did handle it in a meaningful way! So you were lying, the answer to question one was "Yes" and thus you should handle that error. As whenever you can handle an error in a meaningful way, you should do exactly that!
So it does not just depend on the function that fails, it also depends on the purpose of your app. In some cases, you even want to catch and handle malloc(12)
failing, as you can still do something meaningful then, in other cases that is pointless and why would you do pointless things. On the other hand, you could also ensure that all data is saved when the program exits or when the program crashes, using an atexit
or signal handler. In which case it would make no sense again to catch that for every tiny allocation. Yet if your app doesn't even deal with storeable data, e.g. it just fetches something from the network and displays it to the screen, crashing because of a memory failure will have no problematic consequences, other than that your program stops running which it would have to do anyway in case of such an error. So again, it depends.
There is just one thing you should not do: Check for errors that can only appear if external functions work incorrectly. External functions should work as documented. If they don't do, that's a bug and the function is broken but if you cannot even rely on that and even have to test that functions you call are working correctly, what's next? Testing the compiler or the CPU is calculating correctly?
int x = 1 + 1; assert(x == 2);
Seriously? You cannot write programs at all if you cannot rely on certain functionality. If you know for sure that you feed valid input into a function which should never make this function report an error, don't test for an error as this function is not supposed to report one!
try
statement, so you don't have to check every single call or operation. (Also note that some languages are better than others at detecting simple errors like null dereference or array index out of bounds.)errno
! In case you're not familiar, while it's true that "almost all functions from the C library will return 0 or −1 orNULL
when there's an error," they also set the globalerrno
variable, which you can access by using#include <errno.h>
and then simply reading the value oferrno
. So, for example, ifopen
(2) returns-1
, you might want to check whethererrno == EACCES
, which would indicate a permissions error, orENOENT
, which would indicate that the requested file does not exist.try
/catch
, although you could simulate it with jumps.