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.
NULL, errno, and other
library mechanisms.
An error is a problem that prevents a program from performing an operation correctly.
Examples include:
C programs can encounter different types of errors.
Each type may require a different method of finding and correcting the problem.
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.
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.
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.
Runtime errors occur while a program is executing.
Examples include:
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.
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.
scanf() returns the number of input items successfully
assigned.
int age;
if(scanf("%d", &age) == 1)
{
printf("Valid input");
}
else
{
printf("Invalid input");
}
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");
}
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");
}
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.
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.
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");
}
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));
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);
}
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");
}
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.
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);
}
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.
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;
}
Debugging is the process of finding and correcting problems in a program.
Useful techniques include:
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");
}
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.
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.
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;
}
#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;
}
Create a simple student marks program with proper error handling.
This practice will help you combine input validation, return-value checking, file handling, and program termination status.
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.EXIT_SUCCESS and EXIT_FAILURE for program status.Question: Which function is commonly used to print an error message based on the current value of errno?