C Errors

C Errors: Handling and Managing Errors in C

Error handling is a critical part of writing robust C programs. Unlike higher-level languages like Java or Python, C does not have built-in exception handling mechanisms (like try-catch blocks). Instead, C relies on return values, the errno variable, and specific functions to handle errors gracefully.

Understanding how to manage errors is essential for developing secure, stable applications that can handle unexpected situations without crashing.


1. Return Values

The most common way to indicate an error in C is through the return value of a function. By convention, functions that successfully execute return 0, while a non-zero value (often -1 or NULL) indicates that an error has occurred.

Return Value Example

#include <stdio.h>

// Function returns -1 on error int divide(int a, int b, int *result) { if (b == 0) { return -1; // Error: Division by zero } *result = a / b; return 0; // Success }

int main() { int res; if (divide(10, 0, &res) == -1) { printf("Error: Cannot divide by zero!\n"); } else { printf("Result: %d\n", res); } return 0; }


2. Using errno

When a standard C library function fails, it often sets a global variable called errno (error number) to indicate what went wrong. To use errno, you must include the <errno.h> header file.

The value of errno is initially set to 0. It is modified by system calls and standard library functions when an error occurs.

Pro Tip: Always reset errno = 0; before calling a library function if you plan to check its value afterward, as successful calls do not clear previous error codes.


3. The perror() and strerror() Functions

While errno gives you an integer code, it is not very human-readable. C provides two functions to translate these codes into meaningful text:

Error Reporting Example

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

int main() { FILE *file = fopen("nonexistent_file.txt", "r"); if (file == NULL) { // Using perror to print the error perror("Error opening file"); // Using strerror to get the string representation printf("Detailed error: %s\n", strerror(errno)); } else { fclose(file); } return 0; }


4. Best Practices for Error Handling

To ensure your C code is production-ready, follow these best practices:

  1. Always Check Return Values: Never assume that operations like memory allocation (malloc), file handling (fopen), or input reading (scanf) will succeed.
  2. Clean Up Resources: If an error occurs halfway through a function, make sure to free any allocated memory and close open files before returning.
  3. Use Meaningful Error Codes: If you are writing a custom library, define specific error codes (using #define or enum) so other developers understand exactly what failed.

5. Exit Status

When your main() function finishes, it returns an integer to the operating system. Standard practice is to return 0 for success and EXIT_FAILURE (defined in <stdlib.h>) for critical errors.

Exit Status Example

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

int main() { int *ptr = malloc(100 * sizeof(int)); if (ptr == NULL) { fprintf(stderr, "Memory allocation failed!\n"); exit(EXIT_FAILURE); // Immediately terminate the program with an error status } free(ptr); return EXIT_SUCCESS; // Successful termination }


Exercise

?

Which header file must be included to use the errno global variable?