Error Handling in C Programming

Error handling in C is an essential aspect of writing reliable and maintainable code. Since C does not provide built-in exception handling like some higher-level languages, programmers must implement their own error-handling logic using standard tools such as return codes, errno, and assertions.

1. General Approach to Error Handling

In C, most functions signal errors through:

  • Return values (e.g., NULL or -1)
  • Setting global variables like errno
  • Manual checks with conditional statements

Always check the return value of library functions (e.g., memory allocation, file handling, system calls) to ensure that operations have completed successfully.

2. Using errno for Error Reporting

The C standard library defines errno in <errno.h>, a global integer variable that is set by system calls and some library functions to indicate what error occurred.

Key Points:

  • errno is not automatically reset to 0, so set it to 0 before calling a function if you want to check whether an error occurred.
  • After a failure, errno contains an error code (e.g., ENOMEM, EINVAL, EIO).
  • You can use perror() or strerror() to print or interpret the value of errno.

Useful Functions:

Function Description
perror() Prints a human-readable error message to stderr
strerror() Returns a string describing the error code

Common errno Values:

Macro Meaning
ENOMEM Not enough memory
EINVAL Invalid argument
EIO I/O error
EPERM Operation not permitted
ENOENT No such file or directory

Best Practices with errno:

  • Include #include <errno.h> at the top of your program.
  • Set errno = 0 before making a call.
  • After the call, check if it failed (e.g., returned NULL) and inspect errno.
  • Print the error message using perror() or strerror(errno).

3. Using assert for Debugging

The assert macro is defined in <assert.h> and is used to perform sanity checks during development. It evaluates a condition, and if the condition is false, the program prints an error message and terminates.

Syntax:

assert(expression);
  • If expression evaluates to false, the program terminates and displays the line number and file where the failure occurred.
  • Assertions are generally used to validate assumptions during development and testing.
  • They can be disabled in release builds by defining NDEBUG before including <assert.h>.

Best Use Cases for assert:

  • Checking for null pointers.
  • Validating preconditions and postconditions.
  • Ensuring array indices are within bounds.
  • Detecting logic errors during development.

4. Return Code Convention

C programs often follow these return conventions:

  • 0 for success
  • Non-zero for failure

This convention is followed in main() and other functions, helping external tools (shell scripts, compilers, etc.) detect whether a program executed successfully.

5. Summary Table

Tool Purpose Best Used When
errno Identify error from standard/library/system calls When function may fail silently
perror() Print error message When you want detailed error output
strerror() Convert error code to message When you want to display/log messages
assert() Terminate on logic failure For debugging and catching logic errors

Error handling in C may be manual, but it is powerful when done correctly. Proper use of errno and return values helps you write robust and predictable programs. assert helps catch mistakes early during development. By combining careful return value checking, diagnostic tools like errno, and assertions, we can ensure that your C programs behave reliably, even under unexpected circumstances.