Lesson 26
Error Handling
Error handling in C is the process of detecting, reporting, and responding to errors during program execution. C does not provide built-in exception handling; instead, programmers use return values, error codes, and standard library functions such as perror(), strerror(), errno, exit(), and assert().
C Track
Save progress as you learn.
44 lessons
Lesson content
Error Handling in C Programming: A Complete Beginner to Advanced Guide
What is Error Handling in C Programming?
Error handling is the process of detecting, reporting, and responding to errors that occur during program execution. Unlike some modern programming languages, C does not provide built-in exception handling (try, catch, throw). Instead, programmers handle errors by checking return values, using error codes, and utilizing standard library functions.
For example, when opening a file, the operation may fail if the file does not exist. Your program should detect this failure and respond appropriately instead of crashing.
Think of error handling like a traffic signal. When everything is normal, traffic moves smoothly. When something goes wrong, signals help prevent accidents by directing the correct response.
Why Does Error Handling Exist?
Errors can occur for many reasons, including:
- Invalid user input
- File not found
- Memory allocation failure
- Division by zero
- Network failures
- Hardware problems
Error handling helps to:
- Prevent program crashes
- Improve reliability
- Make debugging easier
- Provide meaningful error messages
- Handle unexpected situations gracefully
Real-World Use Cases
Error handling is essential in:
- Banking systems
- File management software
- Operating systems
- Embedded systems
- Database applications
- Network programming
- Medical software
- E-commerce applications
Prerequisites
Before learning Error Handling in C Programming, you should understand:
- Variables
- Functions
- Input and output
- Pointers
- File handling
- Dynamic memory allocation
Core Concepts of Error Handling in C Programming
Types of Errors in C
| Error Type | Description |
|---|---|
| Syntax Errors | Errors detected during compilation |
| Runtime Errors | Errors that occur while the program is running |
| Logical Errors | Errors that produce incorrect results without crashing |
Compile-Time Errors
These errors occur before the program executes.
int number
printf("%d", number);Error: Missing semicolon (;).
Runtime Errors
Runtime errors occur while the program is executing. Examples include:
- Dividing by zero
- Accessing invalid memory
- Opening a missing file
- Memory allocation failure
Logical Errors
Logical errors occur when the program runs successfully but produces incorrect output.
int area = length + width;Correct formula:
int area = length * width;Error Handling Functions
C provides several standard library functions and mechanisms for handling errors.
| Function | Header File | Purpose |
|---|---|---|
| perror() | <stdio.h> | Prints a descriptive error message |
| strerror() | <string.h> | Returns a human-readable error string |
| errno | <errno.h> | Stores the last error code |
| exit() | <stdlib.h> | Terminates the program |
| assert() | <assert.h> | Detects programming errors during development |
The errno Variable
errno is a global variable set by many library functions when an error occurs.
#include <errno.h>
printf("%d", errno);Always check whether the function actually failed before inspecting errno, because not every successful call resets it.
Using perror()
The perror() function prints a custom message followed by a description of the current error.
Syntax
perror("message");Using strerror()
The strerror() function converts an error number into a readable message.
printf("%s", strerror(errno));Using exit()
The exit() function terminates the program immediately.
Syntax
exit(status);Common status values:
| Value | Meaning |
|---|---|
| EXIT_SUCCESS | Successful termination |
| EXIT_FAILURE | Abnormal termination |
Using assert()
The assert() macro helps detect programming mistakes during development.
assert(pointer != NULL);If the condition is false, the program prints diagnostic information and terminates.
Code Examples
Example 1: Beginner – Check Division by Zero
#include <stdio.h> // Include standard input/output library
int main(void) // Program entry point
{
int numerator = 20; // Store the numerator
int denominator = 0; // Store the denominator
if (denominator == 0) // Check whether division is valid
{
printf("Error: Division by zero is not allowed.\n"); // Print an error message
return 1; // Return a non-zero status
}
printf("%d\n", numerator / denominator); // Perform division
return 0; // End the program
}Example 2: Beginner – Handle File Opening Errors
#include <stdio.h> // Include standard input/output library
int main(void) // Program entry point
{
FILE *file = fopen("data.txt", "r"); // Attempt to open the file
if (file == NULL) // Check whether opening failed
{
perror("File Error"); // Print the system error message
return 1; // Exit with an error
}
printf("File opened successfully.\n"); // Print a success message
fclose(file); // Close the file
return 0; // End the program
}Example 3: Intermediate – Using errno and strerror()
#include <stdio.h> // Include standard input/output library
#include <errno.h> // Include error number definitions
#include <string.h> // Include strerror()
int main(void) // Program entry point
{
FILE *file = fopen("sample.txt", "r"); // Attempt to open a file
if (file == NULL) // Check whether opening failed
{
printf("Error Code: %d\n", errno); // Print the error code
printf("Message: %s\n", strerror(errno)); // Print the error description
return 1; // Exit with an error
}
fclose(file); // Close the file
return 0; // End the program
}Example 4: Intermediate – Check Memory Allocation
#include <stdio.h> // Include standard input/output library
#include <stdlib.h> // Include memory allocation functions
int main(void) // Program entry point
{
int *ptr = (int *)malloc(1000000000 * sizeof(int)); // Request a large memory block
if (ptr == NULL) // Check whether allocation failed
{
printf("Memory allocation failed.\n"); // Print an error message
return 1; // Exit with an error
}
printf("Memory allocated successfully.\n"); // Print a success message
free(ptr); // Release the memory
return 0; // End the program
}Example 5: Advanced – Using assert()
#include <stdio.h> // Include standard input/output library
#include <assert.h> // Include assert()
int main(void) // Program entry point
{
int *ptr = NULL; // Declare a null pointer
assert(ptr != NULL); // Verify that the pointer is not NULL
printf("Pointer is valid.\n"); // This line will not execute if the assertion fails
return 0; // End the program
}Note: Assertions are intended for debugging and development. They can be disabled by defining NDEBUG before including <assert.h>.
Common Mistakes and Pitfalls
| Wrong | Correct |
|---|---|
| Ignoring function return values | Always check whether a function succeeded |
| Calling fclose() on a NULL file pointer | Close the file only if it was opened successfully |
| Assuming memory allocation always succeeds | Check if the returned pointer is NULL |
| Using assert() for user input validation | Use normal error handling for runtime conditions |
| Printing generic error messages | Provide meaningful, descriptive messages |
Wrong:
FILE *file = fopen("data.txt", "r");
fclose(file);Correct:
FILE *file = fopen("data.txt", "r");
if (file != NULL)
{
fclose(file);
}
else
{
perror("Unable to open file");
}Best Practices
- Always check the return values of library functions.
- Use perror() or strerror() to display meaningful error messages.
- Validate user input before processing it.
- Release allocated memory even when errors occur.
- Close files after use.
- Return appropriate status codes from functions.
- Use assert() to detect programming errors during development, not for handling expected runtime errors.
- Write clear error messages that help users and developers understand the problem.
When NOT to Use This
Avoid certain error-handling approaches in these situations:
- Don't use assert() to validate user input or file existence; handle these conditions normally.
- Don't terminate the program with exit() for every minor error if recovery is possible.
- Don't ignore error codes returned by standard library functions.
- Don't hide errors by silently continuing after a critical failure.
Whenever possible, recover gracefully instead of terminating the program immediately.
Summary / Key Takeaways
- Error Handling in C programming helps detect and manage problems during program execution.
- C does not provide built-in exception handling like try and catch.
- Always check the return values of functions.
- Use errno to obtain error codes when appropriate.
- Use perror() and strerror() to display readable error messages.
- Use exit() to terminate the program when recovery is not possible.
- Use assert() to detect programming mistakes during development.
- Good error handling makes programs more reliable, maintainable, and user-friendly.
FAQ About Error Handling in C
1. Does C have exception handling like Java or C++?
No. Standard C does not provide try, catch, or throw. Error handling is performed using return values, error codes, and standard library facilities.
2. What is errno?
errno is a global variable that many library functions set when an error occurs. It stores an error code that can be converted into a readable message using strerror() or displayed with perror().
3. What is the difference between perror() and strerror()?
| perror() | strerror() |
|---|---|
| Prints a custom message followed by the system error description | Returns a pointer to the error description as a string |
| Writes directly to the standard error stream | Lets you format or process the message yourself |
4. When should I use assert()?
Use assert() during development to detect programming mistakes, such as invalid assumptions or impossible conditions. It should not be used to handle expected runtime errors like invalid user input.
5. Why should I always check function return values?
Many C library functions indicate success or failure through their return values. Checking these values allows your program to detect errors early, avoid undefined behavior, and respond appropriately.