Segmentation fault (core dumped)
That one line is the most-searched error in C, and it is less mysterious than it looks. Your program asked the processor for a memory address, the operating system checked whether your process is allowed to touch that address, and the answer was no. The kernel then killed the process with a SIGSEGV signal.
The key insight: the crash is a symptom, and its location is often not the bug. The bad pointer was usually created somewhere else, earlier, and this is merely the first place it was used. This page covers the five causes that account for almost every segfault, and then the two tools that find the real line in seconds.
The crashing examples below are deliberately not runnable blocks - they crash by design. Read them, then read the fixed version that follows.
What "Memory You Do Not Own" Means
When your program starts, the operating system maps several regions into its address space: the code, the globals, the stack, and whatever the heap has grown to. Everything else in the address space - including address 0 - is unmapped. Touch an unmapped address, or write to a read-only one, and the hardware traps it.
So a segfault is not the compiler catching you. It is a runtime guard rail, and it only fires when the invalid address happens to be outside your mapped pages. That is why the same bug can crash on one machine and appear to work on another: the memory layout differs.
Cause 1: Dereferencing a NULL Pointer
The most common cause, and the easiest to fix. Address 0 is never mapped, so reading or writing through a null pointer always faults.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = NULL;
*p = 42; /* CRASH: writing to address 0 */
printf("%d\n", *p);
return 0;
}
The realistic version is an unchecked allocation:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *data = malloc(1000000000000UL * sizeof(int)); /* fails, returns NULL */
data[0] = 1; /* CRASH */
free(data);
return 0;
}
malloc returns NULL when it cannot satisfy the request; so does fopen when the file does not exist, and strchr when the character is absent. Check every function that can return NULL before using its result.
Initialize pointers to NULL rather than leaving them uninitialized. A null pointer crashes immediately and obviously; a garbage pointer may corrupt something and crash much later. More on the pattern in null pointers.
Cause 2: Writing Past the End of an Array
C does not check array bounds. Index 10 of a 10-element array is simply the memory after the array, and the compiler will compute that address for you without complaint.
#include <stdio.h>
int main(void) {
int arr[10];
for (int i = 0; i <= 10; i++) { /* <= instead of < : one too many */
arr[i] = i;
}
printf("done\n");
return 0;
}
Whether that crashes is luck. Writing four bytes past a local array usually lands on other stack data - a saved register, another variable, the return address - so the program corrupts itself and crashes later somewhere unrelated. A large overrun leaves the mapped page and segfaults at once.
The wilder version always crashes:
#include <stdio.h>
int main(void) {
int arr[10];
arr[1000000] = 42; /* far outside anything mapped: CRASH */
return 0;
}
The fix is the i < n habit, and computing n rather than typing it twice:
Strings have their own version of this: a buffer with no room for the terminating '\0'.
#include <string.h>
int main(void) {
char name[5];
strcpy(name, "Alexander"); /* 9 chars + terminator into 5 bytes */
return 0;
}
strcpy has no idea how large name is. Use snprintf, which does because you tell it:
Cause 3: Using a Pointer After free (Dangling Pointers)
After free(p), the memory is returned to the allocator. The pointer still holds the old address, but that address is no longer yours.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof *p);
*p = 42;
free(p);
printf("%d\n", *p); /* use after free: may print garbage, may crash */
free(p); /* double free: usually aborts or corrupts the heap */
return 0;
}
The related trap is returning the address of a local variable. Its stack frame is gone the moment the function returns:
#include <stdio.h>
int *make_number(void) {
int value = 42;
return &value; /* the frame dies here; the pointer dangles */
}
int main(void) {
int *p = make_number();
printf("%d\n", *p); /* undefined: garbage, or a crash */
return 0;
}
Two fixes, depending on what you meant. Return the value rather than a pointer, or allocate on the heap and let the caller free it:
Setting the pointer to NULL right after free is the cheap defensive habit: it turns a silent use-after-free into an immediate, obvious null-dereference crash, and it makes a second free(p) harmless, since free(NULL) is defined to do nothing. The allocation side of this story is in dynamic memory and memory leaks.
Cause 4: Stack Overflow from Runaway Recursion
Each function call places a frame on the stack, and the stack is a fixed-size region (commonly 8 MB). Recursion with no base case - or one that is never reached - runs off the end of it.
#include <stdio.h>
int countdown(int n) {
printf("%d\n", n);
return countdown(n - 1); /* no base case: never stops */
}
int main(void) {
return countdown(5);
}
The same happens with a base case that the recursion steps over:
int f(int n) {
if (n == 0) return 1;
return n * f(n - 2); /* from an odd n, never equals 0 */
}
Every recursive function needs a base case that is reachable from every input:
A huge local array does it too - int buffer[10000000]; inside a function asks for 40 MB of stack and faults on the first write. Allocate large buffers on the heap with malloc. See stack vs heap for the sizes involved, and recursion for base-case design.
Cause 5: Writing to a String Literal
This one surprises people because the code looks harmless.
#include <stdio.h>
int main(void) {
char *s = "hello";
s[0] = 'H'; /* CRASH: string literals are read-only */
printf("%s\n", s);
return 0;
}
A string literal lives in a read-only section of the executable. char *s = "hello" points into it; writing through that pointer is a protection fault, which the OS reports as a segfault just like an unmapped access.
The fix is to make an array, which gets its own modifiable copy:
Declaring literal pointers as const char * turns this runtime crash into a compile-time error, which is strictly better. Make it a habit.
Finding the Real Line: gdb
Compile with -g so the executable carries debug symbols, then run it under the debugger:
gcc -g program.c -o program
gdb ./program
Inside gdb:
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555151 in process_item (item=0x0) at program.c:14
14 return item->count * 2;
(gdb) backtrace
#0 process_item (item=0x0) at program.c:14
#1 0x000055555555518a in main () at program.c:23
(gdb) print item
$1 = (struct Item *) 0x0
Three commands do most of the work. run starts the program and stops where it faults. backtrace (or bt) shows the chain of calls that got there - frame #1 is usually where the bad pointer was actually produced. print inspects a variable, and item = 0x0 names the problem outright.
On macOS the equivalent is lldb ./program, then run and bt.
Finding It Faster: AddressSanitizer
Better still, let the compiler instrument the program. AddressSanitizer catches the invalid access at the moment it happens - including the ones that would not have crashed:
gcc -g -fsanitize=address program.c -o program
./program
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010
WRITE of size 4 at 0x602000000010 thread T0
#0 0x4011f6 in main program.c:11
0x602000000010 is located 0 bytes inside of 4-byte region
freed by thread T0 here:
#1 0x4011c9 in main program.c:10
previously allocated by thread T0 here:
#2 0x4011a6 in main program.c:8
That report names the bug class, the line that did it, the line that freed the memory, and the line that allocated it. It is the single most effective debugging tool for C memory bugs, it works with GCC and clang on Linux and macOS, and it costs roughly 2x runtime - which is irrelevant during development.
Pair it with -fsanitize=undefined to catch signed overflow and other undefined behavior at the same time:
gcc -g -Wall -Wextra -fsanitize=address,undefined program.c -o program
valgrind ./program is the alternative that needs no recompilation, and reports the same classes of error plus leaks.
A Checklist When You Hit One
- Rebuild with
-g -Wall -Wextra -fsanitize=address,undefinedand run it again. Most of the time the report names the line and you are done. - If the sanitizer is unavailable, run it under gdb and take a
backtrace. Look at frame 1, not only frame 0. - Check every pointer on the crashing line. Print each one;
0x0identifies a null, and a wild value like0x7fff5fc01000usually means uninitialized or freed. - Ask where that pointer came from. An unchecked
mallocorfopen? An address of a local that has since returned? A pointer used afterfree? - Check every loop bound near the crash for
<=where<was meant. - If the stack trace is thousands of frames deep, it is runaway recursion, not a pointer bug at all.
Preventing Them
The habits that make segfaults rare:
- Compile with
-Wall -Wextraalways, and treat warnings as bugs. - Initialize every pointer, to
NULLif nothing better. - Check the return of
malloc,calloc,realloc, andfopen. - Set pointers to
NULLimmediately after freeing. - Use
snprintfandfgetsrather thansprintfandgets. - Declare string-literal pointers
const char *. - Prefer
sizeof arr / sizeof arr[0]over a hardcoded length. - Run the test suite under AddressSanitizer in CI.
A segfault is the friendly failure mode - it tells you something is wrong. The same class of bug that silently corrupts a neighbouring variable and produces wrong answers three functions later is far worse, and the tools above catch both.
Frequently Asked Questions
What is a segmentation fault in C?
A crash the operating system triggers when your program accesses memory it is not allowed to touch - reading or writing through an invalid pointer, running past the end of an array into an unmapped page, or overflowing the stack. The kernel sends the process a SIGSEGV signal, which terminates it and prints "Segmentation fault (core dumped)".
How do I find where a segmentation fault happens?
Compile with debug symbols and run under a debugger: gcc -g program.c -o program then gdb ./program, run, and when it crashes, backtrace. That prints the exact file and line. Even faster for memory bugs is gcc -g -fsanitize=address program.c -o program - just running the program then prints a full report of what went wrong and where.
Why does my C program segfault only sometimes?
Because the invalid access is undefined behavior, not a guaranteed crash. Writing one element past an array often lands in memory your process does own, so nothing stops you - it corrupts a neighbouring variable instead. You only segfault when the bad address happens to fall outside a mapped page, which depends on the layout of that particular build and run.
Does a segmentation fault mean I have a memory leak?
No - they are opposite problems. A leak is memory you allocated and never freed: the program keeps running and slowly grows. A segfault is touching memory you do not own. Freeing memory twice, or using a pointer after freeing it, causes segfaults; forgetting to free causes leaks.