Der FehlerSegmentierungsfehler (Kern-Dump) in C oder C++ bedeutet, dass Ihr Programm versucht hat, auf den Speicher zuzugreifen, für den es nicht zugelassen ist. Dies ist einer der häufigsten und frustrierendsten C/C++-Fehler. Hier erfahren Sie, wie Sie jede häufige Ursache diagnostizieren und beheben können.
📋 Table of Contents
- Was ein Segfault eigentlich ist
- Ursache 1: Dereferenzierung eines NULL-Zeigers
- Ursache 2: Pufferüberlauf (Zugriff außerhalb der Grenzen)
- Ursache 3: Use-After-Free (baumelnder Zeiger)
- Ursache 4: Stapelüberlauf (unendliche Rekursion)
- Debuggen mit gdb
- Speicherfehler mit Valgrind finden
- Verwenden von AddressSanitizer (schnell, integriert)
- Häufig gestellte Fragen
- Fazit
Was ein Segfault eigentlich ist
Ein Segmentierungsfehler tritt auf, wenn Ihr Programm auf Speicher zugreift, der über den vom Betriebssystem gewährten Speicher hinausgeht – indem es einen ungültigen Zeiger dereferenziert, über die Grenzen eines Arrays hinaus schreibt oder freigegebenen Speicher verwendet. Das Betriebssystem beendet das Programm, um das System zu schützen, und gibt seinen Status aus (den „Core Dump“).
Ursache 1: Dereferenzierung eines NULL-Zeigers
// 🐛 Accessing a NULL or uninitialized pointer
int *ptr = NULL;
*ptr = 42; // ❌ segfault — writing to address 0
int *uninitialized; // points to garbage
*uninitialized = 5; // ❌ segfault (undefined behavior)
// ✅ Always initialize and check pointers
int value = 42;
int *ptr = &value
if (ptr != NULL) {
*ptr = 10; // safe
}
Ursache 2: Pufferüberlauf (Zugriff außerhalb der Grenzen)
// 🐛 Writing past the end of an array
int arr[5];
for (int i = 0; i <= 5; i++) { // ❌ i=5 is out of bounds
arr[i] = i; // arr[5] doesn't exist
}
// ✅ Correct bounds
for (int i = 0; i < 5; i++) { // i < 5, not <=
arr[i] = i;
}
// 🐛 strcpy overflow
char dest[5];
strcpy(dest, "This is too long"); // ❌ overflows dest
// ✅ Use bounded copy
char dest[20];
strncpy(dest, "safe copy", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
Ursache 3: Use-After-Free (baumelnder Zeiger)
// 🐛 Using memory after freeing it
int *ptr = malloc(sizeof(int));
*ptr = 42;
free(ptr);
*ptr = 10; // ❌ segfault — ptr is dangling
// ✅ Set to NULL after freeing
free(ptr);
ptr = NULL; // prevents accidental reuse
// if (ptr) *ptr = 10; // now safely skipped
Ursache 4: Stapelüberlauf (unendliche Rekursion)
// 🐛 Recursion with no base case exhausts the stack
int factorial(int n) {
return n * factorial(n - 1); // ❌ never stops → stack overflow
}
// ✅ Add a base case
int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1);
}
// 🐛 Huge local array can also overflow the stack
void func() {
int huge[10000000]; // ❌ too big for the stack
}
// ✅ Allocate large data on the heap
void func() {
int *huge = malloc(10000000 * sizeof(int));
// ... use it ...
free(huge);
}
Debuggen mit gdb
# Compile with debug symbols
gcc -g -o myprogram myprogram.c
# Run under gdb
gdb ./myprogram
(gdb) run
# When it crashes:
Program received signal SIGSEGV, Segmentation fault.
0x0000... in main () at myprogram.c:15
# See the exact line and call stack
(gdb) backtrace # shows the call stack
(gdb) print ptr # inspect the bad pointer
(gdb) frame 1 # move up the stack
(gdb) list # show surrounding code
Speicherfehler mit Valgrind finden
# Valgrind catches invalid access, leaks, and use-after-free
valgrind --leak-check=full ./myprogram
# Output pinpoints the problem:
# Invalid write of size 4
# at 0x... in main (myprogram.c:15)
# Address 0x0 is not stack'd, malloc'd or (recently) free'd
# It shows exactly where and what kind of memory error occurred
Verwenden von AddressSanitizer (schnell, integriert)
# Compile with AddressSanitizer — catches many bugs at runtime
gcc -fsanitize=address -g -o myprogram myprogram.c
./myprogram
# ASan reports:
# ERROR: AddressSanitizer: heap-buffer-overflow
# WRITE of size 4 at 0x...
# #0 in main myprogram.c:15
# Much faster than Valgrind and often clearer output
Häufig gestellte Fragen
F: Was bedeutet „Core-Dump“?
A: Wenn das Programm abstürzt, schreibt das Betriebssystem einen „Core Dump“ – eine Momentaufnahme des Programmspeichers zum Zeitpunkt des Absturzes. Sie können es in gdb (gdb ./program core) laden, um den Zustand zum Zeitpunkt des Absturzes zu überprüfen.
F: Warum stürzt mein Programm manchmal ab, aber nicht immer?
A: Undefiniertes Verhalten (z. B. die Verwendung von nicht initialisiertem Speicher oder ein leichter Pufferüberlauf) löst möglicherweise nicht immer einen sichtbaren Absturz aus – es hängt davon ab, was sich gerade im Speicher befindet. Das macht solche Bugs gefährlich; Verwenden Sie ASan/Valgrind, um sie zuverlässig zu fangen.
F: gdb vs. Valgrind vs. AddressSanitizer – was soll ich verwenden?
A: AddressSanitizer (Kompilierungsflag) ist am schnellsten und fängt die meisten Speicherfehler ab – beginnen Sie hier. Valgrind erkennt Lecks gründlich und erfordert keine Neukompilierung. gdb dient der interaktiven Untersuchung eines bestimmten Absturzes. Oft verwendet man zuerst ASan und dann gdb, um sich einzuarbeiten.
F: Wie kann ich Segfaults überhaupt verhindern?
A: Initialisieren Sie Zeiger, prüfen Sie vor der Dereferenzierung auf NULL, respektieren Sie Array-Grenzen, setzen Sie Zeiger nach dem Freigeben auf NULL und verwenden Sie moderne intelligente C++-Zeiger (unique_ptr, shared_ptr), die den Speicher automatisch verwalten.
F: Sollte ich in modernem C++ Rohzeiger verwenden?
A: Bevorzugen Sie intelligente Zeiger und Container (Vektor, String), die den Speicher sicher verwalten und die meisten Segfault-Ursachen beseitigen. Rohzeiger sind immer noch nützlich für nicht besitzende Referenzen, aber manuelles Neuen/Löschen ist dort zu vermeiden, wo RAII-Typen funktionieren.
Fazit
Ein Segmentierungsfehler bedeutet einen ungültigen Speicherzugriff – normalerweise ein NULL-/Dangling-Zeiger, ein Pufferüberlauf oder ein Stapelüberlauf. Der Debugging-Workflow:kompilieren mit-fsanitize=address -g, führen Sie es aus (AddressSanitizer ermittelt die genaue Zeile und den Fehlertyp) oder verwenden Siebacktracevon gdb um den Absturzort zu sehen. Um dies zu verhindern: Initialisieren Sie Zeiger, überprüfen Sie NULL, respektieren Sie Grenzen, geben Sie NULL-Out-Zeiger frei und verwenden Sie in C++ intelligente Zeiger und Container. Mit modernen Tools wie ASan können einst mysteriöse Segfaults schnell gefunden und behoben werden.
🔗 Share this article
✍️ Leave a Comment