Runtime Errors and Exceptions in C
Understand runtime errors in C: Division by zero, Segmentation faults, Memory leaks, Buffer overflows. Learn prevention and error handling techniques.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Understand why C has no built-in exceptions
- ✓Identify common runtime errors (division by zero, segfault)
- ✓Prevent memory-related errors (leaks, double free, use after free)
- ✓Use errno, perror(), and strerror() for error handling
- ✓Apply setjmp/longjmp for advanced error recovery
- ✓Use debugging tools like Valgrind and AddressSanitizer
Unlike C++ or Java, C has no built-in exception handling mechanism like try-catch. Runtime errors in C manifest as undefined behavior or program crashes, often resulting in system signals likeSIGFPE or SIGSEGV.
Why Runtime Errors are Dangerous
- •Silent failures: Sometimes produce garbage values instead of crashing
- •Security vulnerabilities: Buffer overflows can be exploited by attackers
- •Hard to reproduce: May only occur with specific inputs or memory states
- •No stack trace: Unlike Java/Python, C doesn't show where the error occurred
Common Runtime Errors in C
| Error Type | Signal | Description |
|---|---|---|
| Division by Zero | SIGFPE | Dividing an integer by zero causes floating-point exception |
| Segmentation Fault | SIGSEGV | Accessing memory outside allocated space (NULL pointer, out of bounds) |
| Stack Overflow | SIGSEGV | Infinite recursion or very large local arrays exhaust stack space |
| Buffer Overflow | Undefined | Writing beyond array bounds corrupts memory |
| Memory Leak | None | Allocated memory never freed, eventually exhausting system memory |
| Double Free | SIGABRT | Calling free() twice on the same pointer |
| Format String Error | Undefined | Wrong format specifier in printf/scanf (%d for long long) |
1. Division by Zero
Dividing an integer by zero is undefined behavior in C. Most systems generate a SIGFPE (Floating Point Exception) signal and terminate the program immediately.
Code That Crashes
1#include <stdio.h>23int main() {4 int a = 5;5 int b = 0;6 7 // RUNTIME ERROR: Division by zero!8 int result = a / b;9 10 printf("Result: %d\n", result); // Never reached11 return 0;12}✓Safe Version with Check
1#include <stdio.h>23int main() {4 int a = 5;5 int b = 0;6 7 // ✓ Always check before dividing8 if (b != 0) {9 int result = a / b;10 printf("Result: %d\n", result);11 } else {12 printf("Error: Cannot divide by zero!\n");13 }14 15 return 0;16}Note: Float Division by Zero
Dividing a float or double by zero doesn't crash — it produces inf (infinity) ornan (not a number). Still, you should avoid it!
2. Segmentation Fault (SIGSEGV)
A segmentation fault occurs when your program tries to access memory it doesn't have permission to access. This is the most common runtime error in C.
Visual: Memory Access
Valid Access ✓
Invalid ✗
Accessing memory outside your array causes SIGSEGV
Cause 2.1: NULL Pointer Dereference
Dereferencing NULL
1#include <stdio.h>23int main() {4 int *ptr = NULL; // Pointer to nothing5 6 // CRASH: Trying to read from address 0x07 printf("%d\n", *ptr);8 9 return 0;10}✓Always Check for NULL
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = malloc(sizeof(int));6 7 // ✓ Check if allocation succeeded8 if (ptr != NULL) {9 *ptr = 42;10 printf("Value: %d\n", *ptr);11 free(ptr);12 } else {13 printf("Memory allocation failed!\n");14 }15 16 return 0;17}Cause 2.2: Array Out of Bounds
Accessing Invalid Index
1#include <stdio.h>23int main() {4 int array[5] = {1, 2, 3, 4, 5}; // Valid: 0 to 45 6 // CRASH: Index 10 doesn't exist!7 array[10] = 999;8 9 printf("%d\n", array[10]);10 return 0;11}(or garbage value / silent corruption)
✓Validate Array Indices
1#include <stdio.h>23#define SIZE 545int main() {6 int array[SIZE] = {1, 2, 3, 4, 5};7 int index = 10;8 9 // ✓ Check bounds before accessing10 if (index >= 0 && index < SIZE) {11 printf("array[%d] = %d\n", index, array[index]);12 } else {13 printf("Error: Index %d out of bounds (0-%d)\n", 14 index, SIZE - 1);15 }16 17 return 0;18}Cause 2.3: Using Freed Memory
Use After Free
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = malloc(sizeof(int));6 *ptr = 42;7 8 free(ptr); // Memory returned to system9 10 // DANGEROUS: ptr still points to freed memory!11 printf("%d\n", *ptr); // Undefined behavior12 *ptr = 100; // May crash or corrupt memory13 14 return 0;15}✓Set to NULL After Free
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = malloc(sizeof(int));6 *ptr = 42;7 printf("Before free: %d\n", *ptr);8 9 free(ptr);10 ptr = NULL; // ✓ Prevent accidental use11 12 // Now any access will clearly crash (NULL deref)13 // instead of silent corruption14 if (ptr != NULL) {15 printf("%d\n", *ptr);16 }17 18 return 0;19}3. Stack Overflow
Stack overflow happens when the call stack exceeds its limit, usually due to infinite recursion or very large local arrays.
Infinite Recursion
1#include <stdio.h>23void infinite() {4 printf("Calling myself...\n");5 infinite(); // No base case - never stops!6}78int main() {9 infinite();10 return 0;11}(Stack exhausted after ~1 million calls)
Huge Local Array
1#include <stdio.h>23int main() {4 // 40MB on stack (typical stack is 1-8MB)5 int huge[10000000];6 7 huge[0] = 1;8 printf("%d\n", huge[0]);9 10 return 0;11}✓Use Heap for Large Arrays
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // ✓ Allocate on heap instead6 int *huge = malloc(10000000 * sizeof(int));7 8 if (huge != NULL) {9 huge[0] = 1;10 printf("%d\n", huge[0]);11 free(huge);12 } else {13 printf("Failed to allocate memory\n");14 }15 16 return 0;17}4. Memory Leaks
A memory leak occurs when you allocate memory but never free it. The program keeps consuming more memory until it runs out.
Memory Leak
1#include <stdlib.h>23void process() {4 int *data = malloc(1000 * sizeof(int));5 // ... use data ...6 7 // LEAK: Forgot to free!8 // Memory is lost when function returns9}1011int main() {12 for (int i = 0; i < 1000000; i++) {13 process(); // Each call leaks memory!14 }15 return 0; // Program may crash from memory exhaustion16}✓Always Free Allocated Memory
1#include <stdlib.h>23void process() {4 int *data = malloc(1000 * sizeof(int));5 if (data == NULL) return;6 7 // ... use data ...8 9 free(data); // ✓ Memory freed10}1112int main() {13 for (int i = 0; i < 1000000; i++) {14 process(); // No leak!15 }16 return 0;17}5. Double Free
Freeing Twice
1#include <stdlib.h>23int main() {4 int *ptr = malloc(sizeof(int));5 *ptr = 42;6 7 free(ptr);8 free(ptr); // CRASH: Double free!9 10 return 0;11}✓Set Pointer to NULL After Free
1#include <stdlib.h>23int main() {4 int *ptr = malloc(sizeof(int));5 *ptr = 42;6 7 free(ptr);8 ptr = NULL; // ✓ Mark as freed9 10 // Safe: free(NULL) does nothing11 free(ptr); // No crash!12 13 return 0;14}6. Format String Errors
Using the wrong format specifier in printf() orscanf() causes undefined behavior.
Wrong Format Specifiers
1#include <stdio.h>23int main() {4 long long bigNum = 9223372036854775807LL;5 6 // Using %d for long long (should be %lld)7 printf("Number: %d\n", bigNum); // Wrong output!8 9 // Missing argument10 printf("Values: %d %d\n", 42); // Undefined!11 12 // Wrong type13 int x = 42;14 printf("Address: %s\n", x); // Crash likely!15 16 return 0;17}✓Correct Format Specifiers
1#include <stdio.h>23int main() {4 int num = 42;5 long long bigNum = 9223372036854775807LL;6 double pi = 3.14159;7 char ch = 'A';8 char *str = "Hello";9 10 printf("int: %d\n", num); // ✓11 printf("long long: %lld\n", bigNum); // ✓12 printf("double: %f\n", pi); // ✓13 printf("char: %c\n", ch); // ✓14 printf("string: %s\n", str); // ✓15 printf("pointer: %p\n", (void*)str);// ✓16 17 return 0;18}Error Handling Techniques in C
Since C has no try-catch, we use manual error checking with return codes, errno, and helper functions.
Method 1: Check Return Values
1#include <stdio.h>2#include <stdlib.h>34int main() {5 FILE *file = fopen("data.txt", "r");6 7 // ✓ Check if fopen succeeded8 if (file == NULL) {9 printf("Error: Could not open file\n");10 return 1;11 }12 13 // ✓ Check if malloc succeeded14 int *data = malloc(100 * sizeof(int));15 if (data == NULL) {16 printf("Error: Memory allocation failed\n");17 fclose(file);18 return 1;19 }20 21 // Use file and data...22 23 free(data);24 fclose(file);25 return 0;26}Method 2: Using errno and perror()
1#include <stdio.h>2#include <errno.h>3#include <string.h>45int main() {6 FILE *file = fopen("nonexistent.txt", "r");7 8 if (file == NULL) {9 // Method 1: perror() - prints description automatically10 perror("fopen failed");11 12 // Method 2: strerror() - get error string13 printf("Error code: %d\n", errno);14 printf("Error message: %s\n", strerror(errno));15 16 return 1;17 }18 19 fclose(file);20 return 0;21}fopen failed: No such file or directory
Error code: 2
Error message: No such file or directory
Method 3: setjmp/longjmp (Advanced)
1#include <stdio.h>2#include <setjmp.h>34jmp_buf jump_buffer;56void dangerous_operation() {7 printf("Doing something risky...\n");8 9 // Simulate an error10 longjmp(jump_buffer, 1); // Jump back with error code 111 12 printf("This never runs\n");13}1415int main() {16 // setjmp returns 0 first time, non-zero when longjmp called17 int error = setjmp(jump_buffer);18 19 if (error == 0) {20 // Normal execution21 dangerous_operation();22 } else {23 // Error handling (jumped here from longjmp)24 printf("Error occurred! Code: %d\n", error);25 }26 27 printf("Program continues...\n");28 return 0;29}Doing something risky...
Error occurred! Code: 1
Program continues...
!Code Pitfalls: Common Mistakes & What to Watch For
Copied code often generates C code that compiles successfully but is riddled with potential runtime errors like null pointer dereferences, division by zero, or buffer overflows.
If you search online to "read numbers from a file and calculate their average," it might produce code that opens a file, reads data, and divides. However, it frequently omits crucial checks: What if `fopen()` fails? What if the file is empty (division by zero)? What if the input is malformed? copied code often handles the "happy path" but ignores edge cases that cause crashes.
The Trap: Online sources are trained on code that "works" for typical inputs. They don't inherently understand C's lack of runtime safety—no exceptions, no garbage collection, no bounds checking. They might use `malloc()` without checking for `NULL`, dereference pointers without validation, or perform arithmetic without overflow checks. The code compiles, so it *seems* correct.
The Reality: In C, you are responsible for all runtime safety. Compiling without errors is only the first step; surviving runtime is the real challenge. Always review copied code for potential runtime errors, add defensive checks, and use tools like Valgrind and AddressSanitizer to catch issues the compiler can't.
Frequently Asked Questions
Q:What is a "Segmentation Fault"?
A: Your program tried to access memory it doesn't have permission to use — usually a NULL pointer, freed memory, or array out of bounds. The OS kills the process to prevent damage.
Q:Why does my program crash only sometimes?
A: Undefined behavior! Memory bugs often depend on random factors like memory layout. Use Valgrind or ASan to find the bug — they make it reproducible.
Q:How do I prevent division by zero?
A: Always check before dividing:if (divisor != 0). For user input, validate and reject zero values. Return an error code or use a default value.
Q:What's the best way to debug runtime errors?
A: 1) Compile with -g flag. 2) Run in GDB to see where it crashes. 3) Use Valgrind/ASan for memory bugs. 4) Add printf statements to narrow down the problem area.
Best Practices Summary
✓ Always Do
- ✓ Check divisor ≠ 0 before division
- ✓ Check pointers for NULL
- ✓ Validate array indices
- ✓ Check malloc/fopen return values
- ✓ Free all allocated memory
- ✓ Set pointers to NULL after free
- ✓ Use correct format specifiers
- ✓ Add base case in recursion
✗ Never Do
- ✗ Divide without checking for zero
- ✗ Dereference NULL pointers
- ✗ Access arrays out of bounds
- ✗ Use memory after free()
- ✗ Forget to free() allocated memory
- ✗ Call free() twice on same pointer
- ✗ Use wrong printf/scanf specifiers
- ✗ Create infinite recursion
Debugging Tools for Runtime Errors
Valgrind (Linux/Mac)
Detects memory leaks, invalid access, use after free
AddressSanitizer (GCC/Clang)
Catches buffer overflows, stack issues
GDB Debugger
Step through code, inspect variables at crash
Compiler Warnings
Catch potential issues at compile time
Test Your Knowledge
Related Tutorials
Types of Errors in C
Master debugging! Learn all error types: Syntax, Semantic, Linker, Runtime, Logical, and Unreachable Code errors with examples and fixes.
Bit Fields in Structures
Pack data efficiently with bit fields! Learn to define structure members that use specific numbers of bits, saving memory in embedded systems and protocols.
Graphics Programming with graphics.h
Learn C graphics programming! Draw circles, ellipses, rectangles, and more using graphics.h. Covers OS/compiler support, setup instructions, and modern alternatives.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!