A segmentation fault (often called a “segfault”) is an error that occurs when a program tries to access a region of memory it isn’t allowed to touch. The operating system kills the offending program immediately, and you’ll typically see a message like “Segmentation fault (core dumped)” in your terminal. If you’re learning C, C++, or any language that gives you direct access to memory, segfaults are one of the most common and frustrating errors you’ll encounter.
What Happens Under the Hood
Your computer’s processor includes a component called the memory management unit (MMU), which acts as a gatekeeper between your program and physical memory. Every time your program reads or writes to a memory address, the MMU checks whether that address is valid and whether your program has permission to access it. If the access is illegal, the MMU raises a hardware fault.
The operating system catches that fault and sends a signal called SIGSEGV (signal 11 on POSIX systems) to the offending process. The default response is to terminate the program and, on many systems, dump a core file containing the program’s memory state at the moment of the crash. That core file is what the “(core dumped)” message refers to, and it’s useful for debugging.
Two things can trigger this chain of events: your program tried to access memory that exists but is off-limits (like trying to write to a read-only section), or your program tried to access memory that doesn’t exist in its address space at all. Either way, the operating system treats it as a violation and shuts you down.
Common Causes
Most segfaults trace back to a handful of coding mistakes:
- Dereferencing a null pointer. If a pointer hasn’t been assigned to valid memory (or was explicitly set to NULL), trying to read or write through it will almost always segfault. This is the single most common cause.
- Using memory after freeing it. Once you call
free()on a block of memory, that address is no longer yours. Accessing it afterward is undefined behavior and frequently causes a segfault, though not always immediately, which makes these bugs tricky to track down. - Array bounds violations. Writing past the end of an array overwrites memory your program doesn’t own. NASA’s programming documentation identifies this as the most common segfault trigger in Fortran programs, and it’s equally common in C.
- Stack overflow. A function that recurses too deeply (or allocates very large arrays on the stack) can exhaust the stack space the OS allocated for your program, resulting in a segfault when the next function call tries to push a new frame.
- Uninitialized pointers. A pointer variable that was declared but never assigned contains garbage data. Dereferencing it means jumping to a random memory address, which will usually land somewhere your program isn’t allowed to go.
The frustrating part is that these errors don’t always crash your program right away. Writing one byte past the end of an array might silently corrupt adjacent data and only cause a segfault minutes later in a completely unrelated function. This gap between the cause and the crash is what makes segfaults notoriously difficult to debug.
Why Some Languages Don’t Have Segfaults
Languages like Java, Python, C#, and Go are called memory-safe because they prevent you from directly manipulating memory addresses. They accomplish this in a few ways: they don’t let you manually deallocate memory (a garbage collector handles that), they don’t let you read or write from arbitrary addresses, and they check every array access to make sure the index is within bounds.
You can still make equivalent mistakes in these languages. Accessing a null reference in Java throws a NullReferenceException, and going past the end of an array throws an IndexOutOfBoundsException. The critical difference is that these errors are predictable and catchable. Your program tells you exactly what went wrong and where. A segfault in C, by contrast, is just one possible outcome of a memory violation. The same bug might silently corrupt data on one run and crash on the next.
Rust takes a different approach: it enforces memory safety rules at compile time, so most of the bugs that cause segfaults in C simply won’t compile. You can opt out of these protections using Rust’s unsafe blocks, but the default is to prevent them entirely.
How to Debug a Segfault
When your program crashes with “Segmentation fault,” the error message itself tells you nothing about where or why it happened. You need additional tools to trace it back to a specific line of code.
The most direct approach is to compile your code with debug symbols (the -g flag in GCC or Clang) and run it inside a debugger like GDB. When the segfault occurs, GDB will pause at the exact instruction that caused it and let you inspect variables, the call stack, and memory. Running bt (backtrace) inside GDB after a crash shows you the chain of function calls that led to the fault.
If you prefer not to use an interactive debugger, Valgrind is an excellent alternative. Compile with -g, then run your program through Valgrind:
valgrind ./your_program
Valgrind will report the exact file and line number of the invalid memory access, along with a stack trace. For example, it might tell you “Invalid read of size 4 at line 52 in broken_linked_list.c,” which points you directly to the problem. Valgrind also catches memory errors that don’t immediately cause a crash, like reading from freed memory or leaking allocations, so it’s worth running even when your program seems to work fine.
A third option is AddressSanitizer, which you enable by compiling with -fsanitize=address. It instruments your code to detect out-of-bounds access, use-after-free, and other memory errors at runtime, with detailed reports when something goes wrong.
Segfaults and Security
A segfault isn’t just a programming annoyance. The same class of memory errors that cause segfaults, particularly buffer overflows, are a major source of security vulnerabilities. An attacker who carefully crafts input to overflow a buffer can potentially overwrite a function’s return address and redirect execution to malicious code.
Modern operating systems have multiple defenses against this. The stack is typically marked as non-executable, so even if an attacker injects code there, the processor won’t run it. Stack canaries (special guard values placed between a function’s local variables and its return address) detect when a buffer overflow has occurred and terminate the program before the corrupted return address is used. Address space layout randomization (ASLR) shuffles where programs and libraries are loaded in memory, making it much harder for an attacker to predict where to redirect execution.
These protections mean that on a modern system, a buffer overflow is more likely to result in a segfault than a successful exploit. But the underlying memory errors are the same, which is why memory safety has become one of the most important topics in software security.

