In C, error handling usually means:
-
Checking return values of functions
-
Using errno for library/system call errors
-
Using special return values (e.g., NULL, -1)
-
Sometimes using setjmp/longjmp for 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:
Many C standard library functions have documented failure return values:
-
malloc() returns NULL on failure
-
fopen() returns NULL on failure
-
printf() returns a negative number on output error
-
scanf() returns the number of items read (or EOF on 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 enum for 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 errno and perror() for system/library calls.
-
For your own functions, document how errors are signaled.
-
Clean up allocated resources before returning on error.
-
Avoid setjmp/longjmp unless you really need non-local jumps.