A segmentation fault means your program tried to access memory it is not allowed to access. The OS terminates the program immediately with no output. Here are the seven situations that cause this most often.
1. Dereferencing a NULL Pointer
#include <stdio.h>
int main() {
int *p = NULL;
printf("%dn", *p); /* segfault — reading address 0 */
return 0;
}
Fix: Check for NULL before dereferencing.
if (p != NULL) {
printf("%dn", *p);
}
This is the most common segfault. It usually comes from a failed malloc, an uninitialized pointer, or a function that returns NULL on error that you forgot to check.
2. Array Out-of-Bounds Access
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printf("%dn", arr[10]); /* accessing memory past the array */
return 0;
}
C does not check array bounds. You will either read garbage, write over adjacent variables, or hit a page boundary and segfault. All three outcomes are bugs — the one that segfaults is the lucky case because at least you know something is wrong.
Fix: Validate your index. Compile with -fsanitize=address to catch this at runtime:
gcc -fsanitize=address -o program program.c
./program
3. Writing to a String Literal
#include <stdio.h>
int main() {
char *str = "hello";
str[0] = 'H'; /* segfault — string literals are read-only */
printf("%sn", str);
return 0;
}
String literals are stored in read-only memory. The pointer str points into that read-only region. Writing to it is undefined behaviour and typically produces a segfault.
Fix: Use a char array instead:
char str[] = "hello"; /* copies to the stack — writable */
str[0] = 'H'; /* fine */
4. Stack Overflow from Infinite Recursion
#include <stdio.h>
void recurse(int n) {
printf("%dn", n);
recurse(n + 1); /* never stops */
}
int main() {
recurse(0);
return 0;
}
Each function call pushes a stack frame. The stack has a fixed size (typically 8MB on Linux). When it fills up, the next write goes past the stack boundary into unmapped memory — segfault.
Fix: Ensure every recursive function has a base case that stops the recursion:
void recurse(int n) {
if (n >= 100) return; /* base case */
printf("%dn", n);
recurse(n + 1);
}
5. Using Memory After free()
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(sizeof(int));
*p = 42;
free(p);
printf("%dn", *p); /* use-after-free — undefined behaviour */
return 0;
}
After free(p), the memory belongs to the allocator. This is closely related to the concept of a dangling pointer,, not you. It may be reallocated for something else. Reading from it returns garbage. Writing to it corrupts the allocator’s internal structures. Either way, a segfault may happen immediately or much later in the program, making this bug hard to track down.
Fix: Set the pointer to NULL after freeing:
free(p);
p = NULL;
/* later access attempts will segfault immediately with a clear cause */
6. Returning a Pointer to a Local Variable
#include <stdio.h>
int *get_value() {
int x = 42;
return &x; /* returning address of a stack variable */
}
int main() {
int *p = get_value();
printf("%dn", *p); /* x no longer exists — undefined behaviour */
return 0;
}
Local variables live on the stack and are destroyed when the function returns. The address you return points to a stack frame that no longer exists. Reading it may return garbage or segfault.
Fix: Allocate on the heap if you need the data to outlive the function:
int *get_value() {
int *p = malloc(sizeof(int));
if (p) *p = 42;
return p; /* caller must free this */
}
7. Double Free
#include <stdlib.h>
int main() {
int *p = malloc(sizeof(int));
free(p);
free(p); /* double free — corrupts the allocator */
return 0;
}
Freeing the same pointer twice corrupts the allocator’s internal bookkeeping. This often causes a segfault either immediately or at some later allocation call. It is also a security vulnerability — double-free bugs can be exploited to gain code execution.
Fix: Set to NULL after free. Freeing NULL is safe (it does nothing):
free(p);
p = NULL;
free(p); /* freeing NULL is defined to be a no-op */
How to Find the Exact Line
Compile with debug symbols and run under GDB:
gcc -g -o program program.c
gdb ./program
(gdb) run
(gdb) backtrace
backtrace shows the call stack at the moment of the crash, with file names and line numbers. Look for the first frame that is your code (not a library function).
Or compile with AddressSanitizer — it catches most of these bugs automatically and prints the exact line:
gcc -g -fsanitize=address -o program program.c
./program
TL;DR
- NULL dereference — check pointers before using them
- Array out of bounds — validate indices, use
-fsanitize=address - Writing to string literals — use
char[]notchar*for mutable strings - Stack overflow — every recursive function needs a base case
- Use-after-free — set pointers to NULL after freeing
- Returning local variable address — allocate on the heap instead
- Double free — set pointer to NULL after first free