C - error handling
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:
-
fopen
returnsNULL
if it fails -
We check it before continuing
Many C standard library functions have documented failure return values:
-
malloc()
returnsNULL
on failure -
fopen()
returnsNULL
on failure -
printf()
returns a negative number on output error -
scanf()
returns the number of items read (orEOF
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
andperror()
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.