段錯誤(Segment Fault!)莫名的問題 by fish

 

段錯誤或段違規(segmentation violation)應該已經很清楚,之前有過一篇文章介紹過“段模型”。

在一般硬件中,段錯誤是由於“內存管理單元”(負責支持虛擬內存的硬件)的異常所致,而該異常則通常是由於解除引用一個未初始化或非法值的指針引起的。如果指針引用一個並不位於你的地址空間中的地址,操作系統便會對此進行干涉。一個小型的會引起段錯誤的程序如下:

int *p = 0;
*p = 17;     //引起一個段錯誤

一個微妙之處是,導致指針具有非法的值通常是由於不同的編程錯誤所引起的。和總線錯誤不同,段錯誤更像是一個間接的症狀而不是引起錯誤的原因。

一個更糟糕的微妙之處是,如果未初始化的指針恰好具有未對齊的值(對於指針所要訪問的數據而言),它將會產生總線錯誤,而不是段錯誤。對於絕大多數架構的計算機而言確實如此,因爲CPU先看到地址,然後再把它發送給MMU(內存管理單元)。 


 

通常導致段錯誤的幾個直接原因:

(1)解除引用一個包含非法值的指針;

(2)解除引用一個空指針(常常由於從系統程序中返回空指針,並未經檢查就使用)。

(3)在未得到正確的權限時進行訪問。例如,試圖往一個只寫的文本段存儲值就會引起段錯誤。

(4)用完了堆棧或堆空間(虛擬內存雖然巨大但絕非無限)。

下面這個說法可能過於簡單,但在絕大多數架構的絕大多數情況下,總線錯誤意味着CPU對進程引用內存的一些做法不滿,而段錯誤則是MMU對進程引用內存的一些情況發生抱怨。

以發生頻率爲序,最終可能導致段錯誤的常見編程錯誤是:

1。壞指針值錯誤:在指針賦值之前就用它來引用內存,或者向庫函數傳送一個壞指針(不要上當!如果編譯器顯示系統程序中出現了段錯誤,並不是因爲系統程序引起了段錯誤,問題很可能還在存在於自己的代碼中)。第三種可能導致壞指針的原因是對指針進行釋放之後再訪問它的內容。可以修改free語句,在指針釋放之後再將它置爲空值。

free(p);  p = NULL;

這樣,如果在指針釋放之後繼續使用該指針,至少程序能在終止之前進行信息轉儲。

2。改寫(overwrite)錯誤:越過數組邊界寫入數據,在動態分配的內存兩端之外寫入數據,或改寫一些堆管理數據結構(在動態分配的內存之前的區域寫入數據就很容易發生這種情況)。

p = malloc(256);  p[-1] = 0;  p[256] = 0;

3。指針釋放引起的錯誤:釋放同一個內存塊兩次,或釋放一塊未曾使用malloc分配的內存,或釋放仍在使用中的內存,或釋放一個無效的指針。一個極爲常見的與釋放內存有關的錯誤就是在 for(p=start; p; p=p->next)這樣的循環中迭代一個鏈表,並在循環體內使用 free(p) 語句。這樣,在下一次循環迭代時,程序就會對已經釋放的指針進行解除引用操作,從而導致不可預料的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章