C - error handling
In C, error handling usually means:
-
Checking return values of functions
-
Using
errnofor library/system call errors -
Using special return values (e.g.,
NULL,-1) -
Sometimes using
setjmp/longjmpfor non-local jumps -
Cleaning up resources manually
2. Return Value Checking
The most common and safest way is to check what the function returns.
Example:
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
printf("Error opening file.\n");
return 1;
}
Here:
-
fopenreturnsNULLif it fails -
We check it before continuing
Many C standard library functions have documented failure return values:
-
malloc()returnsNULLon failure -
fopen()returnsNULLon failure -
printf()returns a negative number on output error -
scanf()returns the number of items read (orEOFon error)
3. errno and <errno.h>
When a function fails, it often sets a global variable called errno to an error code.
Include:
#include <errno.h>
#include <string.h> // for strerror()
Example:
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
printf("Error: %s\n", strerror(errno));
return 1;
}
return 0;
}
strerror(errno) turns the error code into a readable string.
Some common errno values:
-
ENOENT→ No such file or directory -
EACCES→ Permission denied -
ENOMEM→ Out of memory -
EINVAL→ Invalid argument
4. perror() for Quick Error Printing
Instead of using strerror() manually, you can do:
perror("fopen");
This prints:
fopen: No such file or directory
It automatically uses errno.
5. Manual Error Codes
For your own functions, you can:
-
Return special values (
-1,NULL) -
Use an
enumfor error types -
Store error info in a global or passed variable
Example:
enum ErrorCode { SUCCESS, INVALID_INPUT, FILE_ERROR };
enum ErrorCode readData(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return FILE_ERROR;
fclose(fp);
return SUCCESS;
}
6. setjmp and longjmp (Advanced)
C doesn’t have exceptions, but you can simulate jumping out of deep call stacks.
Example:
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
void risky() {
printf("Something went wrong!\n");
longjmp(env, 1); // jump back to where setjmp was called
}
int main() {
if (setjmp(env) == 0) {
risky();
} else {
printf("Recovered from error.\n");
}
return 0;
}
This is rarely needed in normal code — mostly used in libraries or special recovery situations.
7. Best Practices
-
Always check function return values before using them.
-
Use
errnoandperror()for system/library calls. -
For your own functions, document how errors are signaled.
-
Clean up allocated resources before returning on error.
-
Avoid
setjmp/longjmpunless you really need non-local jumps.