Lesson 57 of 60 – Error Handling in C
95%

Error Handling in C

Error handling means detecting, reporting, and responding to problems that can occur while a C program is running. Good error handling makes programs safer, easier to debug, and more reliable.

Note: C does not provide a single built-in exception-handling system like some higher-level languages. C programs commonly handle errors using return values, NULL, errno, and other library mechanisms.

1. What is an Error?

An error is a problem that prevents a program from performing an operation correctly.

Examples include:

  • Invalid input
  • File not found
  • Memory allocation failure
  • Division by zero
  • Invalid pointer access

2. Types of Errors in C

C programs can encounter different types of errors.

  • Syntax errors
  • Compilation errors
  • Linker errors
  • Runtime errors
  • Logical errors

Each type may require a different method of finding and correcting the problem.

3. Syntax Errors

A syntax error occurs when code does not follow the rules of the C language.

int main()
{
    printf("Hello")
    return 0;
}

The printf() statement is missing a semicolon.

4. Compilation Errors

A compilation error occurs when the compiler cannot translate the source code into an object program.

int main()
{
    int number = "Hello";

    return 0;
}

The assignment is incompatible with the declared integer type.

5. Linker Errors

A linker error can occur when a function or variable is declared but the required definition cannot be found during linking.

void greet(void);

int main()
{
    greet();

    return 0;
}

If greet() has no definition in the linked source files or libraries, the linker can report an undefined reference.

6. Runtime Errors

Runtime errors occur while a program is executing.

Examples include:

  • Invalid memory access
  • Division by zero
  • Using an invalid file pointer
  • Memory allocation failure

7. Logical Errors

A logical error occurs when the program runs but produces an incorrect result because the program logic is wrong.

int length = 10;
int width = 5;

int area = length + width;

For a rectangle, the intended area calculation should use multiplication.

8. Checking Return Values

Many C functions report success or failure through their return value. Always check a return value when the operation can fail.

int result = some_function();

if(result == 0)
{
    printf("Operation failed");
}

The exact meaning of the return value depends on the function.

9. Handling scanf() Errors

scanf() returns the number of input items successfully assigned.

int age;

if(scanf("%d", &age) == 1)
{
    printf("Valid input");
}
else
{
    printf("Invalid input");
}

10. NULL Pointer

NULL is commonly used to represent a null pointer value. Functions that return pointers may return NULL when an operation fails.

int *ptr = NULL;

if(ptr == NULL)
{
    printf("Pointer is NULL");
}

11. Checking malloc() Failure

Dynamic memory allocation can fail. Always check the result of malloc(), calloc(), or realloc() before using the returned pointer.

int *numbers = malloc(5 * sizeof(int));

if(numbers == NULL)
{
    printf("Memory allocation failed");
}

12. Using exit()

The exit() function from stdlib.h terminates the program immediately.

#include <stdlib.h>

if(numbers == NULL)
{
    printf("Memory allocation failed");
    exit(EXIT_FAILURE);
}

EXIT_SUCCESS and EXIT_FAILURE provide standard status values for normal and unsuccessful termination.

13. What is errno?

errno is a macro provided by the C library and is used by certain library functions to indicate error information.

#include <errno.h>

A function may set errno when an error occurs. The function's documentation should be checked to determine when errno is meaningful.

14. Using perror()

The perror() function from stdio.h prints a message describing the current value of errno.

#include <stdio.h>

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

if(file == NULL)
{
    perror("File error");
}

15. Using strerror()

The strerror() function from string.h converts an error number into an implementation-defined error message string.

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

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

16. File Opening Error

A file may fail to open because it does not exist, permissions prevent access, or another system-level problem occurs.

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

if(file == NULL)
{
    perror("Unable to open file");
}
else
{
    printf("File opened successfully");
    fclose(file);
}

17. File Read Errors

When reading files, distinguish between reaching the end of a file and encountering an input error.

int ch;

while((ch = fgetc(file)) != EOF)
{
    putchar(ch);
}

if(ferror(file))
{
    printf("File read error");
}

18. Division by Zero

A program should validate the divisor before performing integer division.

int a = 20;
int b = 0;

if(b == 0)
{
    printf("Cannot divide by zero");
}
else
{
    printf("%d", a / b);
}

Checking invalid conditions before performing an operation is an important error-handling technique.

19. Input Validation

Input validation checks whether user-provided data is acceptable before it is used.

int age;

printf("Enter age: ");

if(scanf("%d", &age) != 1)
{
    printf("Invalid input");
}
else if(age < 0)
{
    printf("Age cannot be negative");
}
else
{
    printf("Age = %d", age);
}

20. Return Codes from Functions

A custom function can return a value that represents success or failure.

int divide(int a, int b, int *result)
{
    if(b == 0)
    {
        return 0;
    }

    *result = a / b;
    return 1;
}

The caller can check the returned status before using the result.

21. Using EXIT_SUCCESS and EXIT_FAILURE

The stdlib.h header defines EXIT_SUCCESS and EXIT_FAILURE for use as program termination status values.

#include <stdlib.h>

int main()
{
    if(some_error)
    {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

22. Debugging Errors

Debugging is the process of finding and correcting problems in a program.

Useful techniques include:

  • Read compiler messages carefully
  • Check the line mentioned by the compiler
  • Print variable values
  • Test small parts of the program
  • Use a debugger when available
  • Check return values

23. Avoid Ignoring Errors

Ignoring an error can cause later operations to work with invalid data or invalid resources.

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

/* Do not immediately use file
   without checking whether it is NULL. */

if(file == NULL)
{
    perror("Open failed");
}

24. Avoid Invalid Memory Access

Invalid memory access can cause undefined behavior. Always ensure that pointers are valid and array indexes are within their valid range.

int numbers[5];

for(int i = 0; i < 5; i++)
{
    numbers[i] = i * 10;
}

The valid indexes are 0 through 4.

25. Avoid Memory Leaks

Memory allocated dynamically should eventually be released when it is no longer needed.

int *numbers = malloc(5 * sizeof(int));

if(numbers != NULL)
{
    /* use numbers */

    free(numbers);
    numbers = NULL;
}

Setting the pointer to NULL after freeing it can help avoid accidentally reusing the old pointer value.

26. Error Handling with Functions

A function can return a status and use an output parameter for the actual result.

int calculate(int a, int b, int *result)
{
    if(b == 0)
    {
        return 0;
    }

    *result = a / b;

    return 1;
}

int main()
{
    int result;

    if(calculate(20, 5, &result))
    {
        printf("Result = %d", result);
    }
    else
    {
        printf("Calculation failed");
    }

    return 0;
}

27. Common Error Handling Mistakes

  • Ignoring function return values
  • Using a NULL pointer
  • Using uninitialized variables
  • Accessing arrays outside their bounds
  • Forgetting to free allocated memory
  • Using a closed file
  • Dividing by zero
  • Accepting invalid user input

28. Complete Error Handling Example

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

int main()
{
    int number;
    FILE *file;

    printf("Enter a number: ");

    if(scanf("%d", &number) != 1)
    {
        printf("Invalid input\n");
        return EXIT_FAILURE;
    }

    file = fopen("output.txt", "w");

    if(file == NULL)
    {
        perror("File error");
        return EXIT_FAILURE;
    }

    fprintf(file, "Number = %d\n", number);

    if(fclose(file) != 0)
    {
        perror("Close error");
        return EXIT_FAILURE;
    }

    printf("Data saved successfully\n");

    return EXIT_SUCCESS;
}

29. Good Error Handling Practices

  • Validate input before using it.
  • Check important return values.
  • Check pointers returned by allocation functions.
  • Check file operations for failure.
  • Use meaningful error messages.
  • Release allocated resources.
  • Keep error-handling code clear.
  • Test failure conditions, not only successful cases.

30. Practice Project

Create a simple student marks program with proper error handling.

  1. Ask the user for student marks.
  2. Validate that the input is numeric.
  3. Check that marks are within an acceptable range.
  4. Display an error for invalid marks.
  5. Calculate the result only for valid input.
  6. Save the result to a file.
  7. Check whether the file opened successfully.
  8. Close the file properly.
  9. Display a success or failure message.

This practice will help you combine input validation, return-value checking, file handling, and program termination status.

📌 Key Points

  • Error handling helps programs respond safely to unexpected conditions.
  • C has several types of errors, including syntax, compilation, linker, runtime, and logical errors.
  • Always check important function return values.
  • Check pointers returned by memory allocation functions.
  • Use NULL checks for pointer-returning operations where appropriate.
  • errno can provide error information for certain library functions.
  • perror() can print an error message based on errno.
  • strerror() can convert an error number into a message string.
  • Validate user input before using it.
  • Use EXIT_SUCCESS and EXIT_FAILURE for program status.
  • Release dynamically allocated memory.
  • Check file operations for errors.

🧠 Quick Quiz

Question: Which function is commonly used to print an error message based on the current value of errno?