Error Handling in C
How C handles errors
Many modern languages have try and catch for errors. C does not. Instead, C uses a simpler, older approach: a function tells you it failed through its return value, and it is your job to check that value every time.
On top of that, many standard functions set a special global variable called errno that carries a code explaining why they failed. Together, return-value checking and errno are how reliable C programs stay safe.
Checking return values
The core habit is: after calling a function that can fail, immediately check what it returned before using the result.
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) { // check before using
printf("Error: could not open file.\n");
return 1;
}
// safe to use fp here
fclose(fp);
return 0;
}Error: could not open file.
errno, perror and strerror
When a standard function fails, it often sets errno. You can print a readable description of it in two ways.
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
perror("fopen failed"); // message + errno text
printf("Details: %s\n", strerror(errno)); // same, as string
return 1;
}
fclose(fp);
return 0;
}fopen failed: No such file or directory
Details: No such file or directory
errno only reflects the last failing call. Check it right away, because the next function call may overwrite it.
Handling file errors
File work is a classic place for errors: the file may be missing, locked, or full. Always check fopen, and check reads and writes too.
FILE *fp = fopen("data.txt", "w");
if (fp == NULL) {
perror("Cannot open data.txt");
return 1;
}
if (fprintf(fp, "Hello\n") < 0) { // writing can fail too
perror("Write failed");
}
fclose(fp);See file handling in C for the full set of file functions.
Handling memory errors
malloc can fail when memory runs out. Using its result without checking is a common crash.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = malloc(100 * sizeof(int));
if (arr == NULL) { // check!
printf("Error: out of memory.\n");
return 1;
}
// use arr safely...
free(arr);
return 0;
}See dynamic memory in C for more on malloc and free.
Best practices
- Check the return value of every function that can fail.
- Handle the error close to where it happens, with a clear message.
- Read
errnoimmediately, before another call overwrites it. - Free any resources (files, memory) before exiting on an error.
- Return a non-zero code from
mainto signal failure to the system.
Common mistakes
- Ignoring return values and using a NULL or invalid result.
- Reading
errnotoo late, after another call has changed it. - Printing an error but then continuing as if nothing failed.
- Leaking files or memory by returning early without cleanup.
Write a program that tries to open a file the user names. If it fails, use perror to show why, and return a non-zero exit code. Test it with a real file and a missing file.
Summary
- C has no exceptions — errors are reported through return values.
- Many functions set
errno; read it right after the failing call. perrorprints an error message;strerrorreturns it as a string.- Always check
fopen,malloc, reads and writes. - Clean up resources and return a non-zero code on failure.
Frequently Asked Questions
How does error handling work in C?
try/catch. Instead, functions report problems through their return value — for example returning NULL or -1 on failure — and you check that value after every call. Many library functions also set a global variable called errno with a code describing what went wrong.What is errno in C?
errno is a global variable defined in <errno.h> that many standard functions set when they fail. It holds a number identifying the specific error. You read it right after a failing call, and you can turn it into a readable message with perror or strerror.What is the difference between perror and strerror?
perror("msg") prints your message followed by the text for the current errno, straight to standard error. strerror(errno) instead returns that error text as a string, so you can print it yourself or put it in a log. They describe the same error in different forms.Does C have try and catch like other languages?
try/catch. Error handling is done manually by checking return values and errno. For jumping out of deep error situations there is setjmp/longjmp, but everyday C code relies on checking each function's result.Why should I always check the return value of malloc and fopen?
malloc returns NULL if memory runs out, and fopen returns NULL if the file cannot be opened. If you use the result without checking, the program will likely crash. Checking lets you print a clear message and exit safely instead.