C Programs Tutorials | IT Developer
IT Developer

C Programming - C Error Handling



Share with a Friend

C Programming - C Error Handling

Error Handling in C

Error handling is an important aspect of writing robust and stable C programs. In C, error handling can be done in several ways, depending on the type of error (runtime, logical, or system errors). While C doesn't have built-in exception handling mechanisms like some other languages (e.g., C++ or Java), it provides several ways to detect and handle errors effectively.

Common Methods for Error Handling in C

  1. Return Codes (Error Codes)
  2. Standard Library Functions (errno)
  3. assert() Macro
  4. Error Handling with setjmp() and longjmp()
  5. Custom Error Handling (using exit() and abort())
  1. Return Codes (Error Codes)

One of the simplest ways to handle errors in C is to use return codes from functions to indicate success or failure. The function returns a specific value (often an integer) where 0 typically indicates success, and non-zero values indicate different types of errors.

Example:

C

#include <stdio.h>

int divide(int a, int b) {

    if (b == 0) {

        return -1;  // Error: Division by zero

    }

    return a / b;  // Success: Return the result of division

}

int main() {

    int result = divide(10, 0);

    if (result == -1) {

        printf("Error: Division by zero.\n");

    } else {

        printf("Result: %d\n", result);

    }

    return 0;

}

  • Pros: Simple to implement.
  • Cons: Error codes can be hard to manage and track in large programs, especially if there are many possible errors.
  1. Standard Library Functions (errno)

The C Standard Library provides the errno variable, which is used to set an error code when a system call or library function fails. Many C standard library functions, like fopen(), malloc(), read(), and others, will set errno to indicate the specific error.

The errno.h header defines a set of error codes, and you can use the perror() or strerror() function to print a description of the error.

Example:

C

#include <stdio.h>

#include <errno.h>

#include <string.h>

int main() {

    FILE *file = fopen("nonexistentfile.txt", "r");

    if (file == NULL) {

        // Print the error description using perror

        perror("Error opening file");

        // Alternatively, use strerror with errno

        printf("Error: %s\n", strerror(errno));

    }

    return 0;

}

Common errno values:

  • ENOMEM: Out of memory.
  • EINVAL: Invalid argument.
  • ENOENT: No such file or directory.
  • EIO: I/O error.
  • Pros: Useful for system-level errors, standard for error reporting.
  • Cons: Needs to be manually checked, and errno should be checked immediately after the function call, as it may be overwritten by subsequent library calls.
  1. assert() Macro

The assert() macro is used to detect logical errors in the program during debugging. If the expression passed to assert() is false, the program will terminate, and an error message will be printed that indicates the failed condition and the location of the error.

Example:

C

#include <stdio.h>

#include <assert.h>

int divide(int a, int b) {

    assert(b != 0);  // Check that b is not zero

    return a / b;

}

int main() {

    int result = divide(10, 0);  // This will cause the program to terminate

    return 0;

}

Output:

Assertion failed: b != 0, file error.c, line 6

  • Pros: Great for debugging and detecting bugs during development.
  • Cons: The program is terminated immediately, so it’s not suitable for production error handling. Assertions are typically disabled in release builds (NDEBUG macro).
  1. Error Handling with setjmp() and longjmp()

The setjmp() and longjmp() functions can be used for non-local jumps, which is a form of error handling that can simulate exceptions. setjmp() sets a point in the program to which execution can jump, and longjmp() is used to jump back to the point set by setjmp(), typically for error recovery.

Example:

C

#include <stdio.h>

#include <setjmp.h>

jmp_buf env;

void handle_error() {

    printf("An error occurred, jumping back to main...\n");

    longjmp(env, 1);  // Jump back to the point saved by setjmp

}

int main() {

    if (setjmp(env) == 0) {

        // Initial point, no error yet

        printf("Program started...\n");

        handle_error();  // Simulate an error

    } else {

        // Error handling after jumping

        printf("Program recovered from error.\n");

    }

    return 0;

}

Output:

Program started...

An error occurred, jumping back to main...

Program recovered from error.

  • Pros: Allows for error recovery in certain scenarios.
  • Cons: Complex to use, can make the program flow difficult to understand, and it’s typically avoided in production code.
  1. Custom Error Handling (Using exit() and abort())

C provides the exit() function to terminate a program, typically used in cases where the program cannot continue due to a critical error. The abort() function immediately terminates the program and generates a core dump (useful for debugging).

  • exit(): Used to terminate the program, but before terminating, it cleans up the program resources and calls functions registered with atexit().
  • abort(): Used to immediately terminate the program without cleanup.

Example:

C

#include <stdio.h>

#include <stdlib.h>

int main() {

    int num = -1;

    if (num < 0) {

        printf("Error: Invalid number.\n");

        exit(1);  // Exit the program with error code 1

    }

    printf("Valid number: %d\n", num);

    return 0;

}

  • Pros: Allows for clean program termination.
  • Cons: Stops the program abruptly, making it unsuitable for some scenarios where recovery is needed.

Summary of Error Handling Techniques

Technique Description Use Case
Return Codes

Functions return an error code to indicate success or failure.

Simple functions where failure is rare.

errno

Provides error codes from system calls and standard library.

For handling system-level errors.

assert()

Used for debugging purposes to detect logical errors.

Debugging phase.

setjmp() / longjmp()

Allows for error recovery with non-local jumps.

Complex error handling and recovery.

exit() / abort()

Terminates the program when a critical error occurs.

Critical errors where recovery isn’t possible.

Best Practices for Error Handling in C

  • Always check return values of system calls and library functions.
  • Use errno for system-level errors and provide informative error messages with perror() or strerror().
  • Use assert() for debugging to catch logical errors early in the development phase.
  • Avoid using longjmp() for general error handling due to its complexity.
  • Use exit() or abort() only for critical errors where recovery is not possible.