【C语言】 --- 段错误
參考鏈接
https://blog.csdn.net/xuleilx/article/details/7365424
http://www.blog.chinaunix.net/uid-28414100-id-5759543.html
https://blog.csdn.net/weixin_40005437/article/details/111210315
https://baike.so.com/doc/4335071-4539876.html
一、段錯誤的概念
存儲器區段錯誤(英語:Segmentation fault,經常被縮寫為segfault),又譯為存儲器段錯誤,也稱訪問權限沖突(access violation),是一種程序錯誤。
它會出現在當程序企圖訪問CPU無法尋址的存儲器區段時。當錯誤發生時,硬件會通知操作系統產生了存儲器訪問權限沖突的狀況。操作系統通常會產生核心轉儲(core dump)以方便程序員進行調試。通常該錯誤是由于調用一個地址,而該地址為空(NULL)所造成的,例如鏈表中調用一個未分配地址的空鏈表單元的元素。數組訪問越界也可能產生這個錯誤。(摘抄自wiki)
?
段錯誤就是指訪問的內存超出了系統所給這個程序的內存空間,通常這個值是由gd tr來保存的,他是一個48位的寄存器,其中的32位是保存由它指向的 gdt表,后13位保存 相應于gdt的下標,最后3位包括了程序是否在內存中以及程序的在cpu中的運行級別,指向 的gdt是由以64位為一個單位的表,在這張表中就保存著程序運行的代碼段以及數據段的起 始地址以及與此相應的段限和頁面交換還有程序運行級別還有內存粒度等等的信息。(摘自360百科)
二、編程中常遇到段錯誤的地方有那些
1. 指針指向非法內存
定義了指針變量,但是沒有為其分配內存,即指針沒有指向一塊合法的內存。這里列舉幾個比較隱蔽的例子。
1.1 結構體指針變量的定義和引用錯誤
#include <stdio.h>struct?student {char?*name;int?score; }stu,*pstu;int?main() {strcpy(pstu.name,"Jimy");stu.score?=?99;return?0; }這里的pstu只是一個結構體指針,并沒有分配具體的內存。
直接去引用pstu當然會出錯。
關于結構體變量的定義可以查看我之前的一篇文章
1.2 結構體內部指針變量處理錯誤
還是上面的例子,稍作修改
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h>int main(void) {struct student{char *name = (char *)malloc(sizeof(char) * 10);int score;}stu, *pstu;pstu = (struct student*)malloc(sizeof(struct student));strcpy(pstu->name, "Jimy");pstu->score = 99;free(pstu); system("pause");return 0; }?這里其實涉及到另外一個問題:
結構體在定義的時候,是不能在結構體內部賦值的。
所以這里的name指針雖然在結構體定義時已經分配空間,但是實際編譯時,編譯器不會執行后面malloc分配空間的操作。
?
解決方法:在外面手動分配內存
pstu->name = (char *)malloc(sizeof(char *)* 10);?
2. assert校驗指針參數
assert宏的使用說明:
1. assert是一個宏,而不是函數。
2. assert(表達式)。如果表達式中的值為假,則程序停止運行,并提示出錯。若為真,則繼續運行。
3. assert只在Debug版本起作用,Release版本被編譯器優化掉。
當函數使用指針作為參數的時候,在函數入口處使用 assert(p == NULL)進行校驗。當然這有一個要求,就是p在定義的時候已經初始化為NULL。
比喻上面1.2中的示例,因為name指針并沒有被初始化為NULL,其內部是一個非NULL的亂碼,即使校驗也沒有作用。
2. 指針分配內存太小
為指針分配了內存,但是內存大小不夠,導致出現越界錯誤。
char?*p1?=?“abcdefg”; char?*p2?=?(char?*)malloc(sizeof(char)*strlen(p1)); strcpy(p2,p1);p1 是字符串常量,其長度為7 個字符,但其所占內存大小為8 個byte。初學者往往忘了字符串常量的結束標志“\0”。這樣的話將導致p1 字符串中最后一個空字符“\0”沒有被拷貝到p2 中。解決的辦法是加上這個字符串結束標志符:
char?*p2?=?(char?*)malloc(sizeof(char)*strlen(p1)+1*sizeof(char));3. 分配內存,但并未初始化
?
4. 內存越界
內存分配成功,且已經初始化,但是操作越過了內存的邊界。這種錯誤經常是由于操作數組或指針時出現“多1”或“少1”。比如:
int a[10] = {0};for (i=0; i<=10; i++) {a[i] = i; }5. 內存釋放之后
既然使用free 函數之后指針變量p 本身保存的地址并沒有改變,那我們就需要重新把p的值變為NULL:??p = NULL;?這個NULL 就是我們前面所說的“栓野狗的鏈子”。如果你不栓起來遲早會出問題的。比如:在free(p)之后,你用if(NULL != p)這樣的校驗語句還能起作用嗎?例如:
char?*p?=?(char?*)malloc(100); strcpy(p,?“hello”); free(p);?/*?p?所指的內存被釋放,但是p?所指的地址仍然不變*/ …if?(NULL?!=?p) {/*?沒有起到防錯作用*/strcpy(p,?“world”);?/*?出錯*/ }釋放完塊內存之后,沒有把指針置NULL,這個指針就成為了“野指針”,也有書叫“懸垂指針”。這是很危險的,而且也是經常出錯的地方。所以一定要記住一條:free 完之后,一定要給指針置NULL。同時留一個問題:對NULL 指針連續free 多次會出錯嗎?為什么?如果讓你來設計free函數,你會怎么處理這個問題?
6. 內存已經釋放了,但是繼續通過指針使用
這里一般有三種情況:
第一種:就是上面所說的,free(p)之后,繼續通過p 指針來訪問內存。解決的辦法就是給p 置NULL。
第二種:函數返回棧內存。這是初學者最容易犯的錯誤。比如在函數內部定義了一個數組,卻用return 語句返回指向該數組的指針。解決的辦法就是弄明白棧上變量的生命周期。
第三種:內存使用太復雜,弄不清到底哪塊內存被釋放,哪塊沒有被釋放。解決的辦法是重新設計程序,改善對象之間的調用關系。
三、如何發現程序中的段錯誤并處理
1.以一段簡單的code來重現core dump錯誤
#include <stdio.h>int main(void) {int *p = NULL;printf("%d\n", *p);return 0; }編譯運行,看到Segmentation fault了
再執行ls看下,此時并沒有看到core dumped文件,問題在哪?
問題在于需要需要系統參數設置
ulimit -c 1024再去運行剛才的.out文件就可以看到core dump文件了。
2. 通過gdb查看分析core dump文件
運行gdb命令查看core文件
gdb -c core ./a.out這時候就可以找到錯誤的原因了。
四、利用backtrace和objdump分析段錯誤
?
之后在更新吧。。。。今天太晚了
?
總結
以上是生活随笔為你收集整理的【C语言】 --- 段错误的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3年!我收获了22条ICEM使用经验与网
- 下一篇: CentOS 安装SVN以及可视化管理工