C Error Handling
Error Handling in C
Unlike C++ or Java, C has no built-in exception mechanism. Error handling in C is done through:
- Return values — functions return a special value (e.g.,
-1,NULL,0) to signal failure. errno— a global integer variable set by system calls and library functions when an error occurs.perror()— prints a human-readable error message tostderr.strerror()— returns the error message string for a givenerrnovalue.
Return Value Pattern
The most fundamental C error handling pattern: check the return value of every function that can fail.
#include <stdio.h>
#include <stdlib.h>
// Function returns -1 on error, result on success
int divide(int a, int b, int *result) {
if (b == 0) {
return -1; // error code
}
*result = a / b;
return 0; // success
}
int main() {
int result;
// Success case
if (divide(10, 2, &result) == 0) {
printf("10 / 2 = %d\n", result);
} else {
printf("Error: division failed\n");
}
// Error case
if (divide(10, 0, &result) == 0) {
printf("10 / 0 = %d\n", result);
} else {
printf("Error: cannot divide by zero\n");
}
// malloc returns NULL on failure
int *arr = (int*)malloc(1000000000 * sizeof(int)); // huge allocation
if (arr == NULL) {
fprintf(stderr, "Error: memory allocation failed\n");
return EXIT_FAILURE;
}
free(arr);
return EXIT_SUCCESS;
}
errno, perror() and strerror()
errno is set by system calls when they fail. Always check errno immediately after a failed call — the next function call may overwrite it.
| errno Code | Value | Meaning |
|---|---|---|
ENOENT | 2 | No such file or directory |
EACCES | 13 | Permission denied |
ENOMEM | 12 | Out of memory |
EINVAL | 22 | Invalid argument |
ERANGE | 34 | Result out of range |
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main() {
// Try to open a file that doesn't exist
FILE *fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
// errno is set by fopen on failure
printf("errno value: %d\n", errno);
// perror: prints "prefix: error message" to stderr
perror("fopen failed");
// strerror: returns the error string
printf("Error: %s\n", strerror(errno));
}
// Reset errno before next call
errno = 0;
// Math error: log of negative number
#include <math.h>
double result = sqrt(-1.0);
if (errno == EDOM) {
perror("sqrt(-1)"); // sqrt(-1): Numerical argument out of domain
}
return 0;
}
/*
Output:
errno value: 2
fopen failed: No such file or directory
Error: No such file or directory
*/
Custom Error Handling Pattern
A clean pattern for larger programs: define your own error codes and a centralized error handler.
#include <stdio.h>
#include <stdlib.h>
// Custom error codes
typedef enum {
ERR_OK = 0,
ERR_NULL_PTR = 1,
ERR_DIV_ZERO = 2,
ERR_OVERFLOW = 3,
ERR_IO = 4
} ErrorCode;
// Error message lookup
const char* errorMessage(ErrorCode code) {
switch (code) {
case ERR_OK: return "Success";
case ERR_NULL_PTR: return "Null pointer";
case ERR_DIV_ZERO: return "Division by zero";
case ERR_OVERFLOW: return "Integer overflow";
case ERR_IO: return "I/O error";
default: return "Unknown error";
}
}
// Function using custom error codes
ErrorCode safeDivide(int a, int b, int *out) {
if (out == NULL) return ERR_NULL_PTR;
if (b == 0) return ERR_DIV_ZERO;
*out = a / b;
return ERR_OK;
}
int main() {
int result;
ErrorCode err;
err = safeDivide(10, 2, &result);
if (err == ERR_OK) printf("10/2 = %d\n", result);
else printf("Error: %s\n", errorMessage(err));
err = safeDivide(10, 0, &result);
if (err == ERR_OK) printf("10/0 = %d\n", result);
else printf("Error: %s\n", errorMessage(err)); // Error: Division by zero
err = safeDivide(10, 2, NULL);
if (err == ERR_OK) printf("result = %d\n", result);
else printf("Error: %s\n", errorMessage(err)); // Error: Null pointer
return 0;
}
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.