C - error handling

In C, error handling usually means:

  • Checking return values of functions

  • Using errno for library/system call errors

  • Using special return values (e.g., NULL, -1)

  • Sometimes using setjmp/longjmp for non-local jumps

  • Cleaning up resources manually


2. Return Value Checking

The most common and safest way is to check what the function returns.

Example:

FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    printf("Error opening file.\n");
    return 1;
}

Here:

  • fopen returns NULL if it fails

  • We check it before continuing

Many C standard library functions have documented failure return values:

  • malloc() returns NULL on failure

  • fopen() returns NULL on failure

  • printf() returns a negative number on output error

  • scanf() returns the number of items read (or EOF on error)


3. errno and <errno.h>

When a function fails, it often sets a global variable called errno to an error code.

Include:

#include <errno.h>
#include <string.h> // for strerror()

Example:

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

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

strerror(errno) turns the error code into a readable string.

Some common errno values:

  • ENOENT → No such file or directory

  • EACCES → Permission denied

  • ENOMEM → Out of memory

  • EINVAL → Invalid argument


4. perror() for Quick Error Printing

Instead of using strerror() manually, you can do:

perror("fopen");

This prints:

fopen: No such file or directory

It automatically uses errno.


5. Manual Error Codes

For your own functions, you can:

  • Return special values (-1, NULL)

  • Use an enum for error types

  • Store error info in a global or passed variable

Example:

enum ErrorCode { SUCCESS, INVALID_INPUT, FILE_ERROR };

enum ErrorCode readData(const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (!fp) return FILE_ERROR;
    fclose(fp);
    return SUCCESS;
}

6. setjmp and longjmp (Advanced)

C doesn’t have exceptions, but you can simulate jumping out of deep call stacks.

Example:

#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void risky() {
    printf("Something went wrong!\n");
    longjmp(env, 1); // jump back to where setjmp was called
}

int main() {
    if (setjmp(env) == 0) {
        risky();
    } else {
        printf("Recovered from error.\n");
    }
    return 0;
}

This is rarely needed in normal code — mostly used in libraries or special recovery situations.


7. Best Practices

  • Always check function return values before using them.

  • Use errno and perror() for system/library calls.

  • For your own functions, document how errors are signaled.

  • Clean up allocated resources before returning on error.

  • Avoid setjmp/longjmp unless you really need non-local jumps.