2009-11-17 106 views
5

我在下面的C代碼中得到一個奇怪的結果。malloc()和堆內存

int main() 
{ 
    int *p = (int *) malloc(100); 
    p[120] = 5; 
    printf("\n %d", p[120]); 
} 

因爲我只分配了100個字節,所以這段代碼應該引起分段錯誤。但是,它會打印'5'並且不會給出任何運行時錯誤。任何人都可以請解釋原因?

+4

大概剛好幸運。 – 2009-11-17 19:55:35

+5

你寫了一個5不屬於你的地方。如果那個地方的老闆不喜歡你在家裏做過的事情,它會得到報復。 **當心**,它可能屬於您的USB磁盤驅動器,它將格式化您放入的下一個驅動器。 – pmg 2009-11-17 21:08:26

+4

@pmg:實際上,這在現代保護模式操作系統上不太可能。 – bcat 2009-11-17 23:36:55

回答

20

不,代碼不應該(必然)給出段錯誤。當您嘗試訪問未分配給您的進程的虛擬內存頁面時,會發生段錯誤。

「堆」或「自由存儲」是您的進程擁有的虛擬內存頁面區域。 malloc() API將此區域細分爲塊並返回一個指向該塊的指針。

如果地址超出了指針所在的塊的末尾,那麼您通常會訪問屬於堆的內存,但不是您分配的塊的一部分。通過這種方式,您可以破壞其他堆塊,甚至破壞malloc()用於定義堆的數據結構。

有關堆損壞的更多信息,並檢測它在你的代碼的調試版本的方法,這是一個偉大的書:

Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs by Steve Maguire alt text http://ecx.images-amazon.com/images/I/5148TK6JCVL._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA240_SH20_OU01_.jpg

爲迂腐的增編:在極少數情況下,通過訪問超出堆塊末尾的內存,可以訪問不屬於堆的內存。在這些情況下,您可能會遇到預期的分段錯誤。您也可能會破壞除堆以外的其他數據結構。這確實是一個偶然的問題。但是,與典型的堆塊相比,堆本身非常大,因此99%的時間代碼(如您的示例)會損壞堆。您提供的示例屬於99%的情況。

+0

實際上,因爲他要求改變堆棧末尾的20個字節,取決於架構和周圍的代碼,他可以用軟管來堆棧,甚至改變程序本身。行爲是真正未定義的。 – 2009-11-17 20:47:30

+2

T.E.D.這只是「某種」真實的。毫無疑問,他使用虛擬內存頁面大小的機器。假設可執行代碼具有與堆不同的虛擬機保護。我會打賭他是在這樣一個系統上,因爲C標準庫malloc()函數存在。但是,你寫的是真的,特別是在Z-80上運行TRS-DOS的Radio Shack Model III。 ;) – 2009-11-17 21:15:47

+0

是的,即使在VM頁面系統上,你寫的東西也有可能發生。這不太可能,並且進入這個非常重要的細節並不是很有教育意義。在說明的層面上解決問題。避免瑣事。 – 2009-11-17 21:17:26

0

您正在寫入您尚未分配的內存。如果程序運行時間足夠長,程序最終可能會因堆損壞而受影響。

6

無效的內存訪問並不總是會導致分段錯誤,總線錯誤或其他崩潰。例如,如果在數組之後立即分配了另一個塊,那麼您將更改中的值塊 - 這可能是任何內容。

0

您正在寫入未初始化的內存;這在C中是允許的,這不是一個好主意。這種事情不一定會導致分段錯誤。

+2

您對「允許」一詞的使用具有誤導性。它在語法上有效,但調用未定義的行爲。 – 2011-05-02 13:14:17

3

不,它可以給出一個段錯誤,但只有當內存超出你的進程。否則,它只會修改程序存儲器的其他區域。 C不檢查這個,或者以任何方式保護你,即使在上述這些明顯的情況下。許多軟件開發者都使用C的這個「特性」來重新編寫一個提升了priv特性的程序,並讓自己控制你的機器。它被稱爲buffer overflow exploit

這就是爲什麼C(和C++)應該避免使用新軟件而偏愛Ada等更安全的語言。

+1

我知道最後一句話會讓我投票否定。我不在乎。這是真的。 – 2009-11-17 20:43:34

+0

+1。我完全同意。 – lesscode 2009-11-17 20:47:22

0

段故障的常見原因:

  • 值無效
  • 解引用空指針
  • 試圖寫入到一個只讀段
  • 免費-ING不當指針或不取消引用指針擁有塊。

    int * x = 0;

    x = 200;/原因分段故障*/

段錯誤是由基於什麼被確定爲非法的內存訪問MMU異常產生。根據操作系統如何構建其內存,操作系統上的一個內存訪問可能是合法的(雖然是錯誤的),而在另一個操作系統上它可能是非法的。

+0

這是多年後,但... x = 200很好, * x = 200導致段錯誤。 – 2013-03-21 22:24:36