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