You run your program and it stops. No error message. No line number. On Linux, macOS and WSL you see Segmentation fault (core dumped). On Windows it's quieter: the window just closes, or PowerShell shows an exit code of -1073741819 (the hex value 0xC0000005), which is the same crash under a different name. Git Bash prints Segmentation fault and an exit code of 139.
This post shows how to find the exact line that caused it in about two minutes, then covers the four crashes beginners hit most often. I tested every example here with GCC 16.1 and GDB 17.2 on Windows (MinGW-w64).
What a Segmentation Fault Actually Is
Your program is only allowed to touch memory the operating system gave it. A segmentation fault happens when it reads or writes somewhere else, or writes to memory that is read-only. The operating system stops the program on the spot.
It doesn't tell you which line did it, and the compiler usually can't tell you either. That's why a segfault feels so unhelpful. The fix is a tool that stops the program at the moment of the crash and shows you where it was.
Step 1: Compile With -g
The -g flag adds line numbers to your program, so a debugger can point at your source code instead of at raw addresses. Keep the warning flags on as well:
gcc -g -Wall -Wextra -o program program.c Step 2: Ask gdb for the Exact Line
gdb is a debugger. It comes with MinGW-w64 and WinLibs on Windows, and you can install it on Linux with sudo apt install gdb. Start your program inside it, let it crash, then ask for the backtrace:
gdb ./program (gdb) run (gdb) bt Here is the real output for a program that writes through a NULL pointer (the first example below). The address will be different on your machine, but the last two lines are what you're looking for:
Thread 1 received signal SIGSEGV, Segmentation fault. 0x00007ff60054179e in main () at null.c:7 7 *p = 5; That says the crash happened in main, in the file null.c, on line 7, and it even prints the line. (The line number matches the code as shown in this post. The file on GitHub starts with a one-line comment, so there the same statement is on line 8.) You can also run it in one go with gdb -batch -ex run -ex bt ./program.
The Four Crashes Beginners Hit
Once gdb has shown you the line, the cause is almost always one of these four. The full broken and fixed programs are on GitHub in C-Programing/Troubleshooting/segmentation-fault.
1. Writing Through a NULL Pointer
#include <stdio.h> int main(void) { int *p = NULL; printf("about to write\n"); fflush(stdout); *p = 5; printf("never printed\n"); return 0; } p points at address 0, which no program owns. Writing to it crashes at once. This also happens when malloc or fopen fails and returns NULL and you use the result without looking. The fix is to get real memory and check that you got it:
#include <stdio.h> #include <stdlib.h> int main(void) { int *p = malloc(sizeof *p); if (p == NULL) { fprintf(stderr, "out of memory\n"); return 1; } *p = 5; printf("value: %d\n", *p); free(p); return 0; } 2. Running Past the End of an Array
#include <stdio.h> int main(void) { int nums[3] = {1, 2, 3}; for (int i = 0; i <= 100000; i++) { nums[i] = i; } printf("%d\n", nums[0]); return 0; } The array has 3 elements, but the loop keeps writing to nums[3], nums[4] and on, until it hits memory the program doesn't own. C doesn't check array bounds for you. This one is tricky because GCC printed no warning at all for it, even with -Wall -Wextra. The fix is to stop at the real size:
#include <stdio.h> int main(void) { int nums[3] = {1, 2, 3}; size_t count = sizeof nums / sizeof nums[0]; for (size_t i = 0; i < count; i++) { nums[i] = (int)i; } printf("%d\n", nums[0]); return 0; } 3. Changing a String Literal
#include <stdio.h> int main(void) { char *name = "emre"; name[0] = 'E'; printf("%s\n", name); return 0; } The text "emre" is stored in read-only memory, and name only points at it. Writing to name[0] is a write to read-only memory. The fix is to copy the text into an array you own, which is writable:
#include <stdio.h> int main(void) { char name[] = "emre"; name[0] = 'E'; printf("%s\n", name); return 0; } 4. Infinite Recursion
#include <stdio.h> int depth(int n) { return depth(n + 1) + 1; } int main(void) { printf("%d\n", depth(0)); return 0; } Every call to depth uses a little more of the stack, and nothing ever stops the calls, so the stack runs out. GCC does warn about this one: infinite recursion detected [-Winfinite-recursion]. On Windows the exit code is -1073741571 (0xC00000FD, a stack overflow) instead of the 0xC0000005 you get from the other three. The fix is a base case that ends the recursion:
#include <stdio.h> int depth(int n) { if (n >= 5) { return n; } return depth(n + 1); } int main(void) { printf("%d\n", depth(0)); return 0; } When Your printf Output Disappears
A common trap while debugging: you add printf lines to see how far the program gets, and nothing shows up. When the output is redirected to a file or a pipe, printf text is kept in a buffer and only written later. If the program crashes first, the buffered text is lost.
I tested this with a program that prints two lines and then crashes. Redirected to a file, the file ended up empty. With fflush(stdout); after the printf calls, both lines were saved. Writing to stderr with fprintf(stderr, ...) also worked, because it's not buffered the same way. If you debug with printf, flush after each line.
If You Are on Linux, macOS or WSL
You can also compile with -fsanitize=address -g. AddressSanitizer stops the program at the first bad memory access and prints a report with the line. It's not included in every Windows MinGW build, and mine doesn't have it, so I couldn't test it here. On Linux, macOS or WSL it's worth trying.
The Rule, Stated Once
A segmentation fault means your program touched memory it doesn't own. Compile with -g, run it under gdb, read the line, and then ask what that line is pointing at: NULL, past the end of an array, read-only text, or a stack that ran out.
Two other bugs look similar. A variable you never gave a value prints a garbage value instead of crashing, and forgetting the & in scanf is another classic crash. If you're still learning how input works, start with User Input in C: A Complete Guide for Beginners and scanf vs fgets in C, or go back to Introduction to C programming.
Download the code: the broken and fixed versions of all four are on GitHub in C-Programing/Troubleshooting/segmentation-fault. Compile them with gcc -g -Wall -Wextra and run them yourself.
Originally published on my blog. I post new C, Python and AI-agent guides there regularly.