Skip to main content

C Error Handling

Error handling is a critical aspect of robust software development. In C, error handling mechanisms are relatively simple but effective when used correctly. This page covers how to detect, report, and handle errors in C programs.

Introduction to Error Handling in C

Unlike modern languages with exception handling mechanisms, C relies on return values, global error variables, and user-defined strategies to manage errors. Effective error handling in C requires discipline and consistent coding practices.

Error Detection Methods in C

Return Values

The most common method for error detection in C is through function return values:

c
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
// Handle error
printf("Error opening file!\n");
}

Global Error Variable: errno

C provides a standard global variable called errno (defined in <errno.h>) that is set by system calls and some library functions to indicate what went wrong.

c
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("Error opening file: %s\n", strerror(errno));
return 1;
}

// File operations...
fclose(file);
return 0;
}

Error Reporting Functions

perror()

The perror() function prints a description of the last error that occurred:

c
#include <stdio.h>
#include <errno.h>

int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}

// File operations...
fclose(file);
return 0;
}

strerror()

The strerror() function returns a pointer to the textual representation of the current errno value:

c
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("Error: %s\n", strerror(errno));
return 1;
}

// File operations...
fclose(file);
return 0;
}

Common errno Values

Here are some common errno values defined in <errno.h>:

Errno ValueSymbolic ConstantDescription
1EPERMOperation not permitted
2ENOENTNo such file or directory
13EACCESPermission denied
14EFAULTBad address
22EINVALInvalid argument

Implementing Custom Error Handling

For larger programs, it's often helpful to create a custom error handling system:

c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

// Error levels
#define ERROR_FATAL 0
#define ERROR_WARNING 1
#define ERROR_INFO 2

void handle_error(int level, const char *message) {
switch(level) {
case ERROR_FATAL:
fprintf(stderr, "FATAL ERROR: %s\n", message);
if (errno != 0) {
fprintf(stderr, "System error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
break;

case ERROR_WARNING:
fprintf(stderr, "WARNING: %s\n", message);
if (errno != 0) {
fprintf(stderr, "System error: %s\n", strerror(errno));
}
break;

case ERROR_INFO:
fprintf(stderr, "INFO: %s\n", message);
break;

default:
fprintf(stderr, "UNKNOWN ERROR LEVEL: %s\n", message);
}
}

int main() {
FILE *file = fopen("config.txt", "r");
if (file == NULL) {
handle_error(ERROR_FATAL, "Cannot open configuration file");
}

// Rest of the program...
fclose(file);
return 0;
}

Error Handling in Memory Allocation

Memory allocation is a common source of errors in C programs:

c
#include <stdio.h>
#include <stdlib.h>

int main() {
int *array = (int *)malloc(10 * sizeof(int));
if (array == NULL) {
perror("Memory allocation failed");
return EXIT_FAILURE;
}

// Use the allocated memory...

free(array);
return EXIT_SUCCESS;
}

Best Practices for Error Handling in C

Use the same error handling approach throughout your code. If you decide to use return codes, use them everywhere. If you use errno, check it consistently.

Error Recovery Strategies

In C programming, you might implement several recovery strategies:

  1. Retry Operation: For transient errors like network failures
  2. Use Default Values: When non-critical information is unavailable
  3. Graceful Degradation: Continue with reduced functionality
  4. Clean Exit: When recovery is impossible, release resources and exit

Conclusion

Error handling in C may lack the sophistication of exception-based mechanisms in other languages, but it can be just as effective when implemented properly. By consistently checking return values, using errno appropriately, and developing good error reporting habits, you can create robust C programs that handle failures gracefully.

Remember that good error handling is about:

  • Detecting when something goes wrong
  • Reporting the error clearly
  • Taking appropriate action to recover or exit cleanly
  • Preventing resource leaks

With these principles in mind, your C programs will be more reliable and easier to maintain.

Further Reading



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)