段錯誤相信是每一個C語言初學者都會遇到的一個問題,
很多初學者看到這個錯誤就開始抓狂。
但是沒寫過段錯誤的程序員不是個合格的程序員!
一口君寫了這么多年代碼,有時候還是會出現段錯誤。
下面給大家整理了一些C 語言典型的段錯誤(Segmentation Fault)實例及代碼示例,按常見場景分類說明:
1. 引用空指針
#include int main() { int *p = NULL; printf("%d\n", *p); // 解引用空指針 return 0;}
原因:p 未指向有效內存地址。
2. 訪問受保護的內存地址
int *p = (int*)1; // 強制將指針指向地址 0x1*p = 100; // 訪問系統保護的內存區域
原因:嘗試操作內核或系統保留的內存區域。
3. 修改字符串常量
char *str = "hello"; // 字符串常量存儲在只讀區str[0] = 'H'; // 嘗試修改常量區數據
原因:字符串字面量存儲在只讀內存段,不可被修改6。
4. 棧溢出
void infinite_loop() { infinite_loop(); // 無限遞歸導致棧空間耗盡}int main() { infinite_loop(); }
原因:無限遞歸導致棧內存溢出6。
5. 數組越界訪問
int arr[5];arr[5] = 10; // 合法索引為 0~4,越界訪問無效內存
原因:訪問超出數組定義大小的內存區域。
#include int main() { int arr[5] = {0, 1, 2, 3, 4}; printf("%d\n", arr[10]); // 訪問不存在的元素 return 0;}
執行結果:未定義行為,可能會導致程序崩潰或打印出垃圾值。
數組越界是一些新手最容易出錯的地方,經常因為數組下標控制不好,導致訪問越界,而這種情況可能99%幾率不是立刻報段錯誤,也可能程序運行幾年都不報錯, 但是它一旦報了錯,就會特別隱蔽,非常難查。
剛工作的時候在zte,曾經有2位大佬追一個德國運營商現場報的bug,花了一個月時間,最后發現是數組越界導致。
6. 使用未初始化的指針
int *p; // 未初始化指針*p = 42; // 野指針指向無效地址
原因:指針未指向有效內存空間。
7. 訪問已釋放的內存
int *p = malloc(sizeof(int));free(p);*p = 10; // 內存釋放后繼續使用
原因:操作已被釋放的動態內存區域。
8. 緩沖區溢出
char buffer[5];strcpy(buffer, "HelloWorld"); // 超出 buffer 容量
原因:字符串操作超過目標緩沖區大小。
9. 雙重釋放內存
int *p = malloc(sizeof(int));free(p);free(p); // 重復釋放同一塊內存
原因:多次釋放同一內存導致堆管理器異常。
10. 強制類型轉換錯誤
int num = 42;char *p = (char*)num; // 將整數值強制轉換為地址*p = 'A'; // 訪問非法地址
原因:將非指針類型強制轉換為指針并解引用。
11.格式化字符串與參數類型不匹配示例
int data = 0;sprintf(buf,"%s",data);
12、忘記字符串結尾的空字符示例:
#include int main() { char str[5] = {'H', 'e', 'l', 'l', 'o'}; // 缺少 '\0' printf("%s\n", str); return 0;}
執行結果:未定義行為,可能會打印出亂碼直到遇到一個’\0’。
13、緩沖區溢出示例:
#include #include int main() { char dest[5]; strcpy(dest, "Hello, World!"); // 目標緩沖區太小 printf("%s\n", dest); return 0;}
執行結果:未定義行為,可能會崩潰或覆蓋內存。
14、未檢查類型大小示例:
#include int main() { char *p = (char *)malloc(10 * sizeof(int)); int *q = (int *)p; // 錯誤的假設char和int大小相同 for (int i = 0; i < 10; ++i) { q[i] = i; // 可能導致內存越界 } free(p); return 0;}
執行結果:未定義行為,可能會導致內存越界。
15、變量未正確初始化示例:
#include int main() { int num = 123; printf("%s\n", num); // 錯誤的格式化字符串,應為%d return 0;}
執行結果:未定義行為,可能會打印出任意值。
16、忽視錯誤返回值示例:
#include #include int main() { FILE *file = fopen("nonexistent.txt", "r"); if (!file) { // 忽視錯誤,沒有處理 } // 使用file... fclose(file); return 0;}
執行結果:如果文件不存在,程序會嘗試使用未初始化的指針,可能導致崩潰。
總結
段錯誤本質是訪問了非法內存地址,可通過以下方式避免:
- 初始化指針并檢查有效性;
- 避免越界操作數組或緩沖區;
- 謹慎處理動態內存的分配與釋放;
- 區分常量區與變量區的數據修改權限
- 對一些庫函數返回值一定要判斷