段錯誤(以下定義摘自C專家編程)
段錯誤通常是由於解除引用一個未初始化或非法值的指針引起的。以發生頻率爲序,最終可能導致段錯誤的常見編程錯誤是:
1、壞指針錯誤:在指針賦值之前就用它來引用內存;或者向庫函數傳遞一個壞指針(如果調試器顯示系統程序中出現了段錯誤,很可能並不是系統程序引起的段錯誤,問題可能就出現在自己的代碼中);或者指針被釋放後還繼續訪問它的內容。
2、改寫錯誤:越過數組邊界寫入數據,在動態分配的內存空間以外寫入數據,或改寫一些堆管理數據結構(在動態分配的內存之前的區域寫入數據就很容易發生這種情況)。
3、指針釋放引起的錯誤:釋放同一塊內存兩次,或釋放一塊未曾使用malloc分類的內存,或釋放一個無效的指針。一個極爲常見的與釋放內存有關的錯誤就是在 for(p=start;p;p=p->next) 這樣的循環中迭代一個鏈表,並在循環體內使用 free(p) 這樣的語句。這樣,在下一次循環迭代時,程序就會對已經釋放的指針進行解除引用操作,從而導致不可預料的結果。
問: 以下這段C++代碼會有問題嗎?有幾個printf能正常輸出呢?
#include <stdio.h>
#include <stdlib.h>
class Data {
public :
void test_data() {
printf("test_dtata\n"); // printf2
printf("test_dtata:%d\n", data_); //printf3
}
private :
int data_;
};
class Container {
public :
void test_container() {
printf("test_container\n"); // printf1
data_.test_data();
}
private :
Data data_;
};
int main()
{
Container *c = NULL;
c->test_container();
return 0;
}
答案是有兩個可以輸出,第三個會引起segment fault!
test_container
test_dtata
段錯誤
問: 爲什麼第一第二個printf能輸出,第三個不行?
因爲其實 c->test_container();最終等價於 test_container(NULL);然後調用test_data(NULL) ,結果在
printf("test_dtata:%d\n", data_); //printf3 訪問NULL->data_,顯然這是非法訪問,所以出錯。
問:爲什麼我要舉這個例子?
因爲printf能輸出,直覺就是指針不可能是空的,否則調用不了函數。
我在調試的時候遇到segment fault,具體的項目比這個小例子複雜的多,
一開始是以爲線程同步沒處理好多次釋放了內存導致segment fault,最後發現是未賦值,僅初始化爲NULL)。
如何調試segment fault:
1、 gdb 直接調試,用bt來看segment fault的棧情況
2、 對已產生 core dump的,用gdb program.exe core調試,同樣用bt來看,默認不產生coredump,如何開啓見:http://blog.csdn.net/huangkq1989/article/details/7032776
3、 通過dmesg來看:【引用於: http://zengji.li.blog.163.com/blog/static/87078186201011154156409/】
kernel : *** : segfault at 0000000000000011 rip 00000032f8670454 rsp 00
0000004128fd30 error 4
這種信息一般都是由內存訪問越界造成的,不管是用戶態程序還是內核態程序訪問越界都會出core, 並在系統日誌裏面輸出一條這樣的信息。這條信息的前面分別是訪問越界的程序名,進程ID號,訪問越界的地址以及當時進程堆棧地址等信息,比較有用的信息是 最後的error number. 在上面的信息中,error number是4 ,下面詳細介紹一下error number的信息:
在上面的例子中,error number是6, 轉成二進制就是110, 即bit2=1, bit1=1, bit0=0, 按照上面的解釋,我們可以得出這條信息是由於用戶態程序讀操作訪問越界造成的。
error number是由三個字位組成的,從高到底分別爲bit2 bit1和bit0,所以它的取值範圍是0~7.
bit2: 值爲1表示是用戶態程序內存訪問越界,值爲0表示是內核態程序內存訪問越界
bit1: 值爲1表示是寫操作導致內存訪問越界,值爲0表示是讀操作導致內存訪問越界
bit0: 值爲1表示沒有足夠的權限訪問非法地址的內容,值爲0表示訪問的非法地址根本沒有對應的頁面,也就是無效地址
然後用addr2line -e testseg 0000000000400470 -f可得到出問題的函數及對應的行。