偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

血淚教訓(xùn)!這 17 個(gè) C 語言段錯(cuò)誤陷阱害慘了無數(shù)程序員

開發(fā)
今天咱們來聊聊 C 語言里最讓新手崩潰的東西——段錯(cuò)誤(Segmentation Fault)!

哈嘍大家好!我是小康。

今天咱們來聊聊 C 語言里最讓新手崩潰的東西——段錯(cuò)誤(Segmentation Fault)!

你是不是也有過這樣的經(jīng)歷:代碼寫得好好的,一運(yùn)行就彈出"段錯(cuò)誤",然后程序直接閃退?那種心情就像是精心準(zhǔn)備的飯菜,結(jié)果一上桌就翻了!

別慌!今天我就帶你揪出這些隱藏的"坑",看完之后保證你恍然大悟:"原來如此!"

坑1:空指針的致命一擊

這個(gè)絕對是新手殺手第一名!看這個(gè)代碼:

#include <stdio.h>

int main() {
    int *ptr = NULL;
    
    printf("準(zhǔn)備訪問空指針...\n");
    *ptr = 100;  // ?? 炸了!段錯(cuò)誤!
    printf("這句話永遠(yuǎn)不會(huì)執(zhí)行\(zhòng)n");
    
    return 0;
}

運(yùn)行結(jié)果:

準(zhǔn)備訪問空指針...
Segmentation fault (core dumped)

為什么會(huì)炸? 就像你想往一個(gè)不存在的地址寄快遞一樣,NULL指針指向的是"虛無",你往虛無里塞東西,系統(tǒng)當(dāng)然要炸毛啊!

正確姿勢:

#include <stdio.h>

int main() {
    int *ptr = NULL;
    
    if (ptr != NULL) {  // 先檢查一下
        *ptr = 100;
    } else {
        printf("指針是空的,不能用!\n");
    }
    
    return 0;
}

坑2:數(shù)組越界——跑出安全區(qū)

這個(gè)坑也是相當(dāng)經(jīng)典!就像在游戲里跑出了地圖邊界:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    printf("正常訪問:%d\n", arr[2]);  // 輸出:3
    printf("危險(xiǎn)操作:%d\n", arr[100]); // ?? 可能段錯(cuò)誤!
    
    // 更危險(xiǎn)的寫操作
    arr[10000] = 999;  // ?? 幾乎必定段錯(cuò)誤!
    
    return 0;
}

為什么會(huì)出事? 數(shù)組就像一排房子,你有5間房(索引0-4),結(jié)果你跑到第100間房去放東西,那不是別人家的地盤嗎?系統(tǒng)肯定不答應(yīng)!

安全做法:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int index = 10;
    
    if (index >= 0 && index < 5) {
        printf("安全訪問:%d\n", arr[index]);
    } else {
        printf("索引%d超出范圍了!\n", index);
    }
    
    return 0;
}

坑3:野指針——指向未知的危險(xiǎn)

野指針就像一個(gè)喝醉酒的人,不知道會(huì)指向哪里:

#include <stdio.h>

int main() {
    int *ptr;  // 沒有初始化,是個(gè)野指針!
    
    printf("野指針的值:%p\n", ptr);  // 打印一個(gè)隨機(jī)地址
    *ptr = 42;  // ?? 向隨機(jī)地址寫數(shù)據(jù),危險(xiǎn)!
    
    return 0;
}

// 輸出:
/*
野指針的值:(nil)
Segmentation fault (core dumped)
*/

為什么危險(xiǎn)? 野指針就像一個(gè)沒有目標(biāo)的導(dǎo)彈,你不知道它會(huì)炸到哪里!可能是系統(tǒng)重要的內(nèi)存區(qū)域,那就完蛋了!

正確初始化:

#include <stdio.h>

int main() {
    int value = 10;
    int *ptr = &value;  // 讓指針指向一個(gè)確定的地址
    
    printf("安全操作:%d\n", *ptr);  // 輸出:10
    *ptr = 42;  // 安全的寫操作
    printf("修改后:%d\n", *ptr);    // 輸出:42
    
    return 0;
}

坑4:釋放后繼續(xù)使用——鞭尸行為

這個(gè)錯(cuò)誤就像你把房子賣了,還想回去住一樣:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    
    printf("釋放前:%d\n", *ptr);  // 輸出:100
    
    free(ptr);  // 釋放內(nèi)存
    
    // 致命錯(cuò)誤:繼續(xù)使用已釋放的內(nèi)存
    printf("釋放后:%d\n", *ptr);  // ?? 未定義行為,可能段錯(cuò)誤!
    *ptr = 200;  // ?? 更加危險(xiǎn)!
    
    return 0;
}

正確做法:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    
    printf("使用中:%d\n", *ptr);  // 輸出:100
    
    free(ptr);
    ptr = NULL;  // 釋放后立即置空,防止誤用
    
    if (ptr != NULL) {
        printf("繼續(xù)使用:%d\n", *ptr);
    } else {
        printf("指針已釋放,不能再用了!\n");
    }
    
    return 0;
}

坑5:棧溢出——遞歸的無底洞

遞歸用不好就像掉進(jìn)了無底洞:

#include <stdio.h>

int badRecursion(int n) {
    printf("遞歸層數(shù):%d\n", n);
    return badRecursion(n + 1);  // ?? 永遠(yuǎn)不會(huì)停止!
}

int main() {
    badRecursion(1);  // ?? 棧溢出段錯(cuò)誤!
    return 0;
}

為什么會(huì)炸? 就像一個(gè)人不停地往地下室走,總有一天會(huì)撞到地板!??臻g有限,遞歸太深就會(huì)溢出。

安全的遞歸:

#include <stdio.h>

int safeRecursion(int n) {
    if (n <= 0) {  // 遞歸出口
        return1;
    }
    printf("遞歸層數(shù):%d\n", n);
    return n * safeRecursion(n - 1);
}

int main() {
    int result = safeRecursion(5);
    printf("結(jié)果:%d\n", result);  // 輸出:120
    return 0;
}

坑6:字符串操作越界

字符串操作不小心就會(huì)越界:

#include <stdio.h>
#include <string.h>

int main() {
    char str[5] = "Hi";  // 只能裝4個(gè)字符+'\0'
    
    printf("原字符串:%s\n", str);  // 輸出:Hi
    
    // 危險(xiǎn)操作:拷貝太長的字符串
    strcpy(str, "Hello World!");  // ?? 緩沖區(qū)溢出!
    
    printf("拷貝后:%s\n", str);  // 可能段錯(cuò)誤或垃圾數(shù)據(jù)
    
    return 0;
}
/*
輸出結(jié)果:

原字符串:Hi
拷貝后:Hello World!
*** stack smashing detected ***: terminated
Aborted (core dumped)
*/

安全做法:

#include <stdio.h>
#include <string.h>

int main() {
    char str[20] = "Hi";  // 給足夠的空間
    
    printf("原字符串:%s\n", str);  // 輸出:Hi
    
    // 安全拷貝
    if (strlen("Hello World!") < sizeof(str)) {
        strcpy(str, "Hello World!");
        printf("拷貝后:%s\n", str);  // 輸出:Hello World!
    } else {
        printf("字符串太長,裝不下!\n");
    }
    
    return 0;
}

坑7:返回局部變量地址——過期的房產(chǎn)證

這個(gè)錯(cuò)誤就像把一個(gè)即將拆遷的房子地址給別人:

#include <stdio.h>

int* getDangerousPointer() {
    int localVar = 42;
    return &localVar;  // ?? 返回局部變量地址!
}

int main() {
    int *ptr = getDangerousPointer();
    printf("危險(xiǎn)的值:%d\n", *ptr);  // ?? 可能段錯(cuò)誤或垃圾值!
    
    return 0;
}
/*
輸出:
Segmentation fault (core dumped)
*/

為什么危險(xiǎn)? 局部變量存在棧上,函數(shù)結(jié)束后就被銷毀了。你返回它的地址,就像給別人一個(gè)已經(jīng)被拆掉的房子的鑰匙!

正確做法:

#include <stdio.h>
#include <stdlib.h>

int* getSafePointer() {
    int *ptr = (int*)malloc(sizeof(int));  // 在堆上分配
    *ptr = 42;
    return ptr;  // 安全返回
}

int main() {
    int *ptr = getSafePointer();
    printf("安全的值:%d\n", *ptr);  // 輸出:42
    
    free(ptr);  // 記得釋放
    ptr = NULL;
    
    return 0;
}

坑8:多次釋放同一塊內(nèi)存——重復(fù)拆房子

這就像你把同一棟房子拆了兩次:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    
    printf("使用內(nèi)存:%d\n", *ptr);  // 輸出:100
    
    free(ptr);     // 第一次釋放,正常
    free(ptr);     // ?? 第二次釋放,段錯(cuò)誤!
    
    return0;
}
/*
輸出:
使用內(nèi)存:100
free(): double free detected in tcache 2
Aborted (core dumped)
*/

安全做法:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    
    printf("使用內(nèi)存:%d\n", *ptr);  // 輸出:100
    
    if (ptr != NULL) {
        free(ptr);
        ptr = NULL;  // 釋放后立即置空
    }
    
    if (ptr != NULL) {  // 再次檢查
        free(ptr);
    } else {
        printf("指針已經(jīng)是空的,不需要釋放\n");
    }
    
    return 0;
}

坑9:格式化字符串漏洞

printf系列函數(shù)用不好也會(huì)出事:

#include <stdio.h>

int main() {
    char dangerous[] = "Name: %s, Age: %s, City: %s, Job: %s";
    
     // 危險(xiǎn):直接把用戶輸入當(dāng)格式字符串
    printf(dangerous);  // 4個(gè)%s都會(huì)從棧上取隨機(jī)指針 ?? 可能段錯(cuò)誤!
    return 0;
}

為什么危險(xiǎn)? printf會(huì)按照格式字符串去棧上找參數(shù),但你沒提供參數(shù),它就會(huì)讀取棧上的垃圾數(shù)據(jù),甚至越界訪問!

正確做法:

#include <stdio.h>

int main() {
    char userInput[] = "Name: %s, Age: %s, City: %s, Job: %s";
    
    // 安全:用%s格式化輸出字符串
    printf("%s\n", userInput);
    
    return 0;
}

坑10:忘記檢查malloc返回值

malloc也有失敗的時(shí)候,不檢查就是在玩火:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> 

int main() {
    // 申請超大內(nèi)存,可能失敗
    int *ptr = (int*)malloc(SIZE_MAX);
    
    *ptr = 100;  // ?? 如果malloc失敗,ptr是NULL,段錯(cuò)誤!
    printf("值:%d\n", *ptr);
    
    free(ptr);
    return 0;
}

//輸出:Segmentation fault (core dumped)

安全做法:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    
    if (ptr == NULL) {  // 檢查分配是否成功
        printf("內(nèi)存分配失敗!\n");
        return1;
    }
    
    *ptr = 100;
    printf("值:%d\n", *ptr);  // 輸出:100
    
    free(ptr);
    return 0;
}

坑11:二維數(shù)組指針混亂

二維數(shù)組和指針一起用,新手最容易搞混:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int **matrix;
    
    // 錯(cuò)誤:只分配了指針數(shù)組,沒分配實(shí)際存儲空間
    matrix = (int**)malloc(3 * sizeof(int*));
    
    matrix[0][0] = 10;  // ?? 段錯(cuò)誤!matrix[0]是垃圾值
    
    return 0;
}
// 輸出:Segmentation fault (core dumped)

正確的二維數(shù)組分配:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int **matrix;
    int rows = 3, cols = 4;
    
    // 先分配指針數(shù)組
    matrix = (int**)malloc(rows * sizeof(int*));
    
    // 再為每一行分配空間
    for(int i = 0; i < rows; i++) {
        matrix[i] = (int*)malloc(cols * sizeof(int));
    }
    
    // 現(xiàn)在可以安全使用了
    matrix[0][0] = 10;
    printf("安全賦值:%d\n", matrix[0][0]);  // 輸出:10
    
    // 釋放內(nèi)存
    for(int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);
    
    return 0;
}

坑12:結(jié)構(gòu)體內(nèi)指針未初始化

結(jié)構(gòu)體里的指針成員經(jīng)常被忘記初始化:

#include <stdio.h>
#include <stdlib.h>

struct Student {
    char *name;
    int age;
};

int main() {
    struct Student stu;
    stu.age = 18;
    
    // 危險(xiǎn):name指針沒有初始化就使用
    strcpy(stu.name, "小明");  // ?? 可能段錯(cuò)誤!
    
    return 0;
}

正確做法:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student {
    char *name;
    int age;
};

int main() {
    struct Student stu;
    stu.age = 18;
    
    // 先為name分配空間
    stu.name = (char*)malloc(20 * sizeof(char));
    strcpy(stu.name, "小明");
    
    printf("學(xué)生姓名:%s,年齡:%d\n", stu.name, stu.age);
    
    free(stu.name);  // 記得釋放
    return 0;
}

坑13:函數(shù)參數(shù)傳遞陷阱

指針作為參數(shù)傳遞時(shí)的常見錯(cuò)誤:

#include <stdio.h>
#include <stdlib.h>

void allocateMemory(int *ptr) {
    ptr = (int*)malloc(sizeof(int));  // ?? 這樣寫沒用!
    *ptr = 100;
}

int main() {
    int *myPtr = NULL;
    allocateMemory(myPtr);
    
    printf("值:%d\n", *myPtr);  // ?? 段錯(cuò)誤!myPtr還是NULL
    
    return 0;
}
// 輸出:Segmentation fault (core dumped)

正確的指針參數(shù)傳遞:

#include <stdio.h>
#include <stdlib.h>

void allocateMemory(int **ptr) {  // 傳遞指針的指針
    *ptr = (int*)malloc(sizeof(int));
    **ptr = 100;
}

int main() {
    int *myPtr = NULL;
    allocateMemory(&myPtr);  // 傳遞指針的地址
    
    printf("值:%d\n", *myPtr);  // 輸出:100
    
    free(myPtr);
    return 0;
}

坑14:字符串字面量修改

這個(gè)坑特別隱蔽,很多人不知道:

#include <stdio.h>

int main() {
    char *str = "Hello";  // 字符串字面量存在只讀區(qū)域
    
    printf("原字符串:%s\n", str);  // 輸出:Hello
    
    str[0] = 'h';  // ?? 試圖修改只讀內(nèi)存,段錯(cuò)誤!
    
    return 0;
}

// 輸出:原字符串:Hello
// Segmentation fault (core dumped)

正確做法:

#include <stdio.h>

int main() {
    char str[] = "Hello";  // 數(shù)組,可以修改
    // 或者 char str[10] = "Hello";
    
    printf("原字符串:%s\n", str);  // 輸出:Hello
    
    str[0] = 'h';  // 安全修改
    printf("修改后:%s\n", str);    // 輸出:hello
    
    return 0;
}

坑15:聯(lián)合體內(nèi)存覆蓋陷阱

union使用不當(dāng)也會(huì)造成意外:

#include <stdio.h>

union Data {
    int intVal;
    float floatVal;
    char *strVal;
};

int main() {
    union Data data;
    
    data.strVal = "Hello";
    printf("字符串:%s\n", data.strVal);  // 輸出:Hello
    
    data.intVal = 100;  // 覆蓋了strVal的值
    printf("整數(shù):%d\n", data.intVal);    // 輸出:100
    
    // 危險(xiǎn):strVal現(xiàn)在是垃圾值
    printf("字符串:%s\n", data.strVal);  // ?? 可能段錯(cuò)誤!
    
    return 0;
}
/* 輸出:
字符串:Hello
整數(shù):100
Segmentation fault (core dumped)
*/

安全使用聯(lián)合體:

#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;
    
    // 設(shè)置字符串
    data.type = TYPE_STRING;
    data.value.strVal = "Hello";
    
    if (data.type == TYPE_STRING) {
        printf("字符串:%s\n", data.value.strVal);
    }
    
    // 改為整數(shù)類型
    data.type = TYPE_INT;
    data.value.intVal = 100;
    
    if (data.type == TYPE_INT) {
        printf("整數(shù):%d\n", data.value.intVal);
    }
    
    return 0;
}

坑16:函數(shù)指針未檢查

函數(shù)指針為NULL時(shí)調(diào)用會(huì)段錯(cuò)誤:

#include <stdio.h>

void sayHello() {
    printf("Hello!\n");
}

int main() {
    void (*funcPtr)() = NULL;
    
    // 某些情況下可能給funcPtr賦值,某些情況下可能忘記
    if (rand() % 2 == 0) {
        funcPtr = sayHello;
    }
    
    funcPtr();  // ?? 如果funcPtr還是NULL,段錯(cuò)誤!
    
    return 0;
}

安全調(diào)用函數(shù)指針:

#include <stdio.h>

void sayHello() {
    printf("Hello!\n");
}

void sayGoodbye() {
    printf("Goodbye!\n");
}

int main() {
    void (*funcPtr)() = NULL;
    
    // 根據(jù)條件設(shè)置函數(shù)指針
    if (rand() % 2 == 0) {
        funcPtr = sayHello;
    } else {
        funcPtr = sayGoodbye;
    }
    
    // 安全調(diào)用
    if (funcPtr != NULL) {
        funcPtr();
    } else {
        printf("函數(shù)指針為空,無法調(diào)用!\n");
    }
    
    return 0;
}

坑17:競態(tài)條件導(dǎo)致的段錯(cuò)誤

多線程環(huán)境下的段錯(cuò)誤更難調(diào)試:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int *global_ptr = NULL;

void* thread_func(void* arg) {
    if (global_ptr != NULL) {
        sleep(1);
        // ?? 另一個(gè)線程可能在這里釋放了global_ptr
        *global_ptr = 100;  // 可能段錯(cuò)誤!
    }
    return NULL;
}

int main() {
    pthread_t thread;
    
    global_ptr = (int*)malloc(sizeof(int));
    
    pthread_create(&thread, NULL, thread_func, NULL);
    
    // 主線程釋放內(nèi)存
    free(global_ptr);
    global_ptr = NULL;
    
    pthread_join(thread, NULL);
    return 0;
}

正確做法:

#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);  // 模擬一些操作
        *global_ptr = 100;
        printf("設(shè)置值成功:%d\n", *global_ptr);
    }
    return NULL;
}

int main() {
    pthread_t thread;
    
    global_ptr = (int*)malloc(sizeof(int));
    
    pthread_create(&thread, NULL, thread_func, NULL);
    
    // 等子線程完成再釋放!這才是關(guān)鍵
    pthread_join(thread, NULL);
    
    // 現(xiàn)在可以安全釋放了
    free(global_ptr);
    global_ptr = NULL;
    
    return 0;
}

避坑終極秘籍

看完這 17 個(gè)經(jīng)典陷阱,相信你已經(jīng)對段錯(cuò)誤有了全新的認(rèn)識!讓我總結(jié)幾個(gè)終極避坑秘籍:

內(nèi)存管理黃金法則:

  • malloc必須配free - 有借有還,再借不難
  • free后立即置NULL - 防止野指針復(fù)活
  • 使用前必須檢查 - NULL指針是大敵
  • 邊界時(shí)刻要注意 - 數(shù)組越界是噩夢

指針使用終極口訣:

  • 初始化 - 聲明時(shí)就給個(gè)明確的值
  • 驗(yàn)證 - 使用前檢查是否為NULL
  • 保護(hù) - 操作時(shí)注意邊界和權(quán)限
  • 清理 - 用完立即釋放并置空

多級指針避坑技巧:

  • 二維數(shù)組 - 先分配行指針,再分配每行數(shù)據(jù)
  • 函數(shù)傳參 - 想改指針本身,就傳指針的地址
  • 結(jié)構(gòu)體指針 - 內(nèi)部的指針成員別忘了初始化

字符串操作安全守則:

  • 只讀區(qū)別碰 - 字符串字面量不能修改
  • 空間要充足 - strcpy前確保目標(biāo)夠大
  • 邊界要檢查 - 防止緩沖區(qū)溢出

高級避坑技巧:

  • 聯(lián)合體慎用 - 記住類型,避免數(shù)據(jù)覆蓋
  • 函數(shù)指針檢查 - NULL函數(shù)指針不能調(diào)用
  • 多線程加鎖 - 共享資源要保護(hù)

記住這些,再配合調(diào)試工具(gdb、valgrind、AddressSanitizer),段錯(cuò)誤再也不是你的攔路虎!

責(zé)任編輯:趙寧寧 來源: 跟著小康學(xué)編程
相關(guān)推薦

2025-06-09 10:05:00

C 語言指針編程

2019-07-10 09:12:20

程序員級別跳槽

2019-06-18 11:09:54

2010-10-18 11:39:41

程序員

2009-02-12 15:07:57

程序員創(chuàng)業(yè)經(jīng)驗(yàn)

2025-03-03 12:00:00

異步編程C#開發(fā)

2021-09-29 09:07:22

Docker 日志容器

2010-11-04 11:06:34

程序員

2019-07-02 09:30:31

程序員勞動(dòng)陷阱

2020-09-11 14:48:43

RedisMySQL數(shù)據(jù)

2020-11-09 07:38:19

RedisMySQL互聯(lián)網(wǎng)

2018-03-15 10:21:50

程序員面試低級錯(cuò)誤

2009-04-20 10:16:22

IT職場程序員面試

2010-01-14 18:07:30

C++語言

2019-03-21 14:45:37

C語言開發(fā)應(yīng)用

2012-04-25 09:14:57

C++

2025-03-12 01:35:00

同步編程模型

2021-09-02 08:40:10

程序員錯(cuò)誤

2014-05-13 13:09:23

Python程序員

2020-07-22 14:30:50

程序員財(cái)富螞蟻金服
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號