Fundamentals 18 min read

17 Common C Segmentation Fault Pitfalls and How to Avoid Them

This article walks through seventeen classic C programming mistakes that cause segmentation faults—such as null pointers, out‑of‑bounds array access, wild pointers, double frees, and format string bugs—and provides safe, production‑ready code examples to prevent each crash.

Liangxu Linux
Liangxu Linux
Liangxu Linux
17 Common C Segmentation Fault Pitfalls and How to Avoid Them

Pitfall 1: Null Pointer Dereference

Dereferencing a NULL pointer causes an immediate segmentation fault.

#include <stdio.h>
int main(){
    int *ptr = NULL;
    if (ptr != NULL) {
        *ptr = 100;
    } else {
        printf("Pointer is null, cannot dereference!
");
    }
    return 0;
}

Pitfall 2: Array Out‑of‑Bounds

Accessing an index outside the declared range corrupts memory.

#include <stdio.h>
int main(){
    int arr[5] = {1,2,3,4,5};
    int index = 2;
    if (index >= 0 && index < 5) {
        printf("Safe access: %d
", arr[index]);
    } else {
        printf("Index %d out of range!
", index);
    }
    return 0;
}

Pitfall 3: Wild Pointer

A pointer that is never initialized points to random memory.

#include <stdio.h>
int main(){
    int value = 10;
    int *ptr = &value; // points to a valid object
    printf("Safe operation: %d
", *ptr);
    *ptr = 42;
    printf("After change: %d
", *ptr);
    return 0;
}

Pitfall 4: Use‑After‑Free (Dangling Pointer)

Freeing memory and then continuing to use the pointer leads to undefined behavior.

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *ptr = malloc(sizeof(int));
    *ptr = 100;
    printf("Before free: %d
", *ptr);
    free(ptr);
    ptr = NULL; // immediately nullify
    if (ptr != NULL) {
        printf("Will not run
");
    } else {
        printf("Pointer is null, cannot use!
");
    }
    return 0;
}

Pitfall 5: Unbounded Recursion (Stack Overflow)

Recursive functions without a proper base case exhaust the stack.

#include <stdio.h>
int safeRecursion(int n){
    if (n <= 0) return 1; // base case
    printf("Recursion depth: %d
", n);
    return n * safeRecursion(n-1);
}
int main(){
    int result = safeRecursion(5);
    printf("Result: %d
", result);
    return 0;
}

Pitfall 6: String Copy Overflow

Copying a longer string into a too‑small buffer overruns memory.

#include <stdio.h>
#include <string.h>
int main(){
    char str[20] = "Hi";
    printf("Original: %s
", str);
    if (strlen("Hello World!") < sizeof(str)) {
        strcpy(str, "Hello World!");
        printf("After copy: %s
", str);
    } else {
        printf("String too long to copy!
");
    }
    return 0;
}

Pitfall 7: Returning Address of a Local Variable

Returning a pointer to a stack‑allocated variable leaves a dangling reference.

#include <stdio.h>
#include <stdlib.h>
int* getSafePointer(){
    int *ptr = malloc(sizeof(int));
    *ptr = 42;
    return ptr;
}
int main(){
    int *ptr = getSafePointer();
    printf("Safe value: %d
", *ptr);
    free(ptr);
    ptr = NULL;
    return 0;
}

Pitfall 8: Double Free

Freeing the same memory block twice corrupts the allocator's metadata.

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *ptr = malloc(sizeof(int));
    *ptr = 100;
    printf("Using memory: %d
", *ptr);
    if (ptr != NULL) {
        free(ptr);
        ptr = NULL;
    }
    if (ptr != NULL) {
        free(ptr); // will not execute
    } else {
        printf("Pointer already null, no need to free
");
    }
    return 0;
}

Pitfall 9: Format String Vulnerability

Passing user‑controlled data directly as a format string lets attackers read/write arbitrary memory.

#include <stdio.h>
int main(){
    char userInput[] = "Name: %s, Age: %s, City: %s, Job: %s";
    printf("%s
", userInput); // treat as plain data
    return 0;
}

Pitfall 10: Ignoring malloc Return Value

Assuming malloc always succeeds can lead to dereferencing a NULL pointer.

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *ptr = malloc(sizeof(int));
    if (ptr == NULL) {
        printf("Memory allocation failed!
");
        return 1;
    }
    *ptr = 100;
    printf("Value: %d
", *ptr);
    free(ptr);
    return 0;
}

Pitfall 11: Mis‑managed 2‑D Array Pointers

Allocating only the pointer array without allocating each row leaves dangling pointers.

#include <stdio.h>
#include <stdlib.h>
int main(){
    int rows = 3, cols = 4;
    int **matrix = malloc(rows * sizeof(int*));
    for (int i = 0; i < rows; i++) {
        matrix[i] = malloc(cols * sizeof(int));
    }
    matrix[0][0] = 10;
    printf("Safe assign: %d
", matrix[0][0]);
    for (int i = 0; i < rows; i++) free(matrix[i]);
    free(matrix);
    return 0;
}

Pitfall 12: Uninitialized Struct Pointer Member

Using a struct field that points to unallocated memory causes crashes.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student{ char *name; int age; };
int main(){
    struct Student stu;
    stu.age = 18;
    stu.name = malloc(20 * sizeof(char));
    strcpy(stu.name, "小明");
    printf("Student: %s, %d
", stu.name, stu.age);
    free(stu.name);
    return 0;
}

Pitfall 13: Pointer Parameter Mis‑use

Assigning memory to a pointer parameter without passing its address leaves the caller's pointer unchanged.

#include <stdio.h>
#include <stdlib.h>
void allocateMemory(int **ptr){
    *ptr = malloc(sizeof(int));
    **ptr = 100;
}
int main(){
    int *myPtr = NULL;
    allocateMemory(&myPtr);
    printf("Value: %d
", *myPtr);
    free(myPtr);
    return 0;
}

Pitfall 14: Modifying String Literals

String literals reside in read‑only memory; attempting to modify them causes a fault.

#include <stdio.h>
int main(){
    char str[] = "Hello"; // mutable copy
    printf("Original: %s
", str);
    str[0] = 'h';
    printf("Modified: %s
", str);
    return 0;
}

Pitfall 15: Unsafe Union Usage

Writing to one union member overwrites the others, leading to garbage values.

#include <stdio.h>
enum DataType{ TYPE_INT, TYPE_FLOAT, TYPE_STRING };
struct SafeData{ enum DataType type; union{ int intVal; float floatVal; char *strVal; } value; };
int main(){
    struct SafeData data;
    data.type = TYPE_STRING;
    data.value.strVal = "Hello";
    if (data.type == TYPE_STRING) printf("String: %s
", data.value.strVal);
    data.type = TYPE_INT;
    data.value.intVal = 100;
    if (data.type == TYPE_INT) printf("Int: %d
", data.value.intVal);
    return 0;
}

Pitfall 16: Null Function Pointer Call

Calling a function pointer that is still NULL triggers a crash.

#include <stdio.h>
#include <stdlib.h>
void sayHello(){ printf("Hello!
"); }
void sayGoodbye(){ printf("Goodbye!
"); }
int main(){
    void (*funcPtr)() = NULL;
    if (rand() % 2 == 0) funcPtr = sayHello; else funcPtr = sayGoodbye;
    if (funcPtr != NULL) funcPtr();
    else printf("Function pointer is null, cannot call!
");
    return 0;
}

Pitfall 17: Race Condition in Multithreaded Code

One thread may free memory while another still accesses it, causing a segmentation fault.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int *global_ptr = NULL;
void* thread_func(void* arg){
    if (global_ptr != NULL) {
        sleep(1); // simulate work
        *global_ptr = 100;
        printf("Set value successfully: %d
", *global_ptr);
    }
    return NULL;
}
int main(){
    pthread_t thread;
    global_ptr = malloc(sizeof(int));
    pthread_create(&thread, NULL, thread_func, NULL);
    pthread_join(thread, NULL); // wait before freeing
    free(global_ptr);
    global_ptr = NULL;
    return 0;
}

Ultimate Cheat Sheet

Memory Management Golden Rules

malloc must be paired with free – always release what you allocate.

Set pointer to NULL after free – prevents accidental reuse.

Check pointers before use – guard against NULL dereference.

Watch boundaries – never read or write past array limits.

Pointer Usage Mantra

Initialize – give every pointer a known value at declaration.

Validate – test for NULL before dereferencing.

Protect – respect array bounds and memory permissions.

Clean up – free and nullify when done.

Advanced Tips

Use unions cautiously – keep track of the active member.

Check function pointers – never call a NULL pointer.

Lock shared resources in multithreaded programs.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Memory ManagementCpointerssegmentation fault
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.