Introduction
Program errors are mistakes or flaws in computer programs that prevent them from functioning correctly. Understanding different types of errors, their causes, and how to identify and fix them is crucial for effective programming. Errors can occur at different stages of program development and execution, each requiring specific approaches for resolution.
Key Concepts
Error: A mistake in a program that produces incorrect results or prevents execution Bug: Common term for program errors Exception: Runtime error that can be handled by the program Fault: Design flaw that may lead to errors under certain conditions Failure: Observable incorrect behavior caused by errors Error Handling: Techniques to manage and recover from errors
Types of Program Errors
1. Syntax Errors (Compile-time Errors)
Missing Semicolons
#include <stdio.h>
int main() {
int x = 10 // Error: Missing semicolon
int y = 20; // Error: Previous line affects this
printf("Sum: %d\n", x + y);
return 0;
}
// Compiler Error:
// error: expected ';' before 'int'
Incorrect Brackets and Parentheses
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++ { // Error: Missing closing parenthesis
printf("%d ", arr[i]);
}
if (arr[0] > 0 { // Error: Missing closing parenthesis
printf("Positive\n");
}
return 0;
// Error: Missing closing brace for main function
Undeclared Variables
#include <stdio.h>
int main() {
int x = 10;
printf("Value of y: %d\n", y); // Error: 'y' undeclared
z = x + 5; // Error: 'z' undeclared
return 0;
}
Type Mismatches
#include <stdio.h>
int main() {
int number;
char *text = "Hello";
number = text; // Error: Cannot assign string to int
int arr[3.5]; // Error: Array size must be integer
return 0;
}
2. Runtime Errors (Execution Errors)
Division by Zero
#include <stdio.h>
int main() {
int a = 10;
int b = 0;
int result = a / b; // Runtime error: Division by zero
printf("Result: %d\n", result);
return 0;
}
// Improved version with error checking
int safeDivision() {
int a = 10;
int b = 0;
if (b != 0) {
int result = a / b;
printf("Result: %d\n", result);
} else {
printf("Error: Division by zero attempted\n");
return -1;
}
return 0;
}
Array Index Out of Bounds
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// Valid access
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// Runtime error: Array index out of bounds
printf("arr[10] = %d\n", arr[10]); // Accessing invalid memory
arr[15] = 100; // Writing to invalid memory
return 0;
}
// Safe array access
void safeArrayAccess() {
int arr[5] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
int index = 10;
if (index >= 0 && index < size) {
printf("arr[%d] = %d\n", index, arr[index]);
} else {
printf("Error: Index %d is out of bounds (0-%d)\n", index, size-1);
}
}
Null Pointer Dereference
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = NULL;
*ptr = 10; // Runtime error: Dereferencing NULL pointer
printf("Value: %d\n", *ptr);
return 0;
}
// Safe pointer usage
void safePointerUsage() {
int *ptr = malloc(sizeof(int));
if (ptr != NULL) {
*ptr = 10;
printf("Value: %d\n", *ptr);
free(ptr);
ptr = NULL; // Prevent accidental reuse
} else {
printf("Error: Memory allocation failed\n");
}
}
Memory Allocation Failures
#include <stdio.h>
#include <stdlib.h>
int main() {
// Attempting to allocate large amount of memory
size_t huge_size = SIZE_MAX;
int *ptr = malloc(huge_size);
*ptr = 10; // Runtime error if malloc failed
printf("Value: %d\n", *ptr);
free(ptr);
return 0;
}
// Proper memory allocation with error checking
void properMemoryAllocation() {
size_t size = 1000 * sizeof(int);
int *ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Error: Failed to allocate %zu bytes\n", size);
return;
}
// Use the allocated memory
for (int i = 0; i < 1000; i++) {
ptr[i] = i;
}
printf("Memory allocated and used successfully\n");
free(ptr);
}
3. Logic Errors (Semantic Errors)
Incorrect Algorithm Implementation
#include <stdio.h>
// Incorrect implementation of factorial
int wrongFactorial(int n) {
int result = 0; // Should be 1, not 0
for (int i = 1; i <= n; i++) {
result += i; // Should be multiplication, not addition
}
return result;
}
// Correct implementation
int correctFactorial(int n) {
if (n < 0) return -1; // Handle negative input
if (n == 0 || n == 1) return 1;
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
int main() {
int num = 5;
printf("Wrong factorial of %d: %d\n", num, wrongFactorial(num));
printf("Correct factorial of %d: %d\n", num, correctFactorial(num));
return 0;
}
Off-by-One Errors
#include <stdio.h>
void demonstrateOffByOneErrors() {
int arr[5] = {10, 20, 30, 40, 50};
printf("Wrong loop (off-by-one error):\n");
for (int i = 1; i <= 5; i++) { // Should start at 0, not 1
printf("arr[%d] = %d\n", i, arr[i]); // Will access arr[5] which is out of bounds
}
printf("\nCorrect loop:\n");
for (int i = 0; i < 5; i++) { // Correct bounds
printf("arr[%d] = %d\n", i, arr[i]);
}
}
// String processing with off-by-one error
void stringProcessingError() {
char str[] = "Hello";
int len = strlen(str);
printf("Wrong string reversal:\n");
for (int i = 0; i <= len; i++) { // Should be i < len
printf("%c", str[len - i]); // Will access str[-1] and str[5]
}
printf("\nCorrect string reversal:\n");
for (int i = len - 1; i >= 0; i--) { // Correct implementation
printf("%c", str[i]);
}
printf("\n");
}
Infinite Loops
#include <stdio.h>
void demonstrateInfiniteLoop() {
int i = 10;
// Infinite loop - condition never becomes false
while (i > 0) {
printf("Value: %d\n", i);
i++; // Should be i-- to decrease
// Safety break to prevent actual infinite loop in demo
if (i > 15) break;
}
}
// Correct loop implementation
void correctLoop() {
int i = 10;
while (i > 0) {
printf("Value: %d\n", i);
i--; // Correct decrement
}
printf("Loop completed\n");
}
4. Linking Errors
Undefined References
// main.c
#include <stdio.h>
// Function declared but not defined
int calculateSum(int a, int b);
void displayResult(int result);
int main() {
int result = calculateSum(10, 20); // Linking error: undefined reference
displayResult(result); // Linking error: undefined reference
return 0;
}
// To fix: Create separate file with implementations
// utils.c
int calculateSum(int a, int b) {
return a + b;
}
void displayResult(int result) {
printf("Result: %d\n", result);
}
Multiple Definitions
// file1.c
int globalVariable = 10; // Definition
// file2.c
int globalVariable = 20; // Error: Multiple definition
// To fix: Use extern in header and define in one source file
// header.h
extern int globalVariable;
// file1.c
int globalVariable = 10; // Single definition
// file2.c includes header.h and uses extern declaration
Error Detection and Prevention
1. Compiler Warnings
#include <stdio.h>
int main() {
int x = 10;
float y = 3.14;
int result = x + y; // Warning: implicit conversion
if (x = 5) { // Warning: assignment in condition
printf("x is 5\n");
}
return 0;
}
// Compile with warnings: gcc -Wall -Wextra program.c
2. Static Analysis Tools
#include <stdio.h>
#include <stdlib.h>
void demonstrateStaticAnalysisIssues() {
int *ptr = malloc(sizeof(int));
*ptr = 10;
// Memory leak - forgot to free
// Static analysis tools would detect this
int arr[10];
for (int i = 0; i <= 10; i++) { // Buffer overflow
arr[i] = i; // Static analysis would warn
}
int x;
printf("Uninitialized: %d\n", x); // Use of uninitialized variable
}
// Corrected version
void correctedVersion() {
int *ptr = malloc(sizeof(int));
if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return;
}
*ptr = 10;
printf("Value: %d\n", *ptr);
free(ptr); // Proper cleanup
int arr[10];
for (int i = 0; i < 10; i++) { // Correct bounds
arr[i] = i;
}
int x = 0; // Proper initialization
printf("Initialized: %d\n", x);
}
3. Runtime Error Detection
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf jump_buffer;
void segfault_handler(int sig) {
printf("Caught segmentation fault! Attempting recovery...\n");
longjmp(jump_buffer, 1);
}
int main() {
signal(SIGSEGV, segfault_handler);
if (setjmp(jump_buffer) == 0) {
// Dangerous operation
int *ptr = NULL;
*ptr = 10; // This will cause segfault
} else {
// Recovery path
printf("Recovered from error, continuing execution\n");
}
printf("Program completed successfully\n");
return 0;
}
Error Handling Strategies
1. Return Code Checking
#include <stdio.h>
#include <stdlib.h>
int safeFileOperation(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
perror("Error opening file");
return -1; // Error code
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), file)) {
printf("%s", buffer);
}
if (fclose(file) != 0) {
perror("Error closing file");
return -2; // Different error code
}
return 0; // Success
}
int main() {
int result = safeFileOperation("example.txt");
switch (result) {
case 0:
printf("File operation successful\n");
break;
case -1:
printf("Failed to open file\n");
break;
case -2:
printf("Failed to close file\n");
break;
default:
printf("Unknown error\n");
}
return 0;
}
2. Error Logging
#include <stdio.h>
#include <time.h>
#include <stdarg.h>
void logError(const char *format, ...) {
FILE *logFile = fopen("error.log", "a");
if (logFile == NULL) return;
time_t now = time(NULL);
fprintf(logFile, "[%s] ERROR: ", ctime(&now));
va_list args;
va_start(args, format);
vfprintf(logFile, format, args);
va_end(args);
fprintf(logFile, "\n");
fclose(logFile);
}
int riskyOperation(int value) {
if (value < 0) {
logError("Invalid input value: %d", value);
return -1;
}
if (value > 1000) {
logError("Value too large: %d (max: 1000)", value);
return -2;
}
return value * 2;
}
int main() {
int result1 = riskyOperation(-5);
int result2 = riskyOperation(2000);
int result3 = riskyOperation(50);
printf("Results: %d, %d, %d\n", result1, result2, result3);
return 0;
}
Best Practices for Error Prevention
- Always check return values of functions that can fail
- Initialize variables before use
- Validate input parameters in functions
- Use compiler warnings and treat them seriously
- Test boundary conditions and edge cases
- Use static analysis tools to catch potential issues
- Implement proper error handling and recovery mechanisms
- Write unit tests to verify code correctness
- Use version control to track changes and revert if needed
- Code reviews to catch errors before deployment
Summary
Program errors are classified into syntax errors (caught during compilation), runtime errors (occur during execution), logic errors (produce incorrect results), and linking errors (occur during the linking phase). Understanding these error types and implementing proper error detection, handling, and prevention strategies is essential for writing robust programs. Good programming practices, thorough testing, and appropriate tools help minimize errors and improve code quality.
Part of BCA Programming with C Course (UGCOA22J201)