2013-01-31 44 views
0

可能重複:
behaviour of malloc(0)C程序幫助:內存分配不足,但仍然有效......爲什麼?

我想了解下內存分配因此,我與malloc試驗。我爲這個指針分配了0個字節,但它仍然可以保存一個整數。事實上,無論我把什麼號碼放入malloc的參數中,它仍然可以容納我給它的任何號碼。爲什麼是這樣?

#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    int *ptr = (int*)malloc(0); 

    *ptr = 9; 

    printf("%i", *ptr); // 9 

    free(ptr); 

    return 0; 
} 

它仍然打印9,這是怎麼回事?

+2

這是未定義的行爲。任何事情都可能發生,包括似乎工作。 –

回答

3

如果大小爲0,則malloc()會返回NULL,或者返回一個可以稍後成功傳遞給free()的唯一指針值 。

我猜你正在打第二個案子。 無論如何,那只是錯誤的指針碰巧在一個你可以寫入而沒有產生分段錯誤的區域,但是你可能正在寫一些其他變量的空間來搞亂它的值。

+0

我在這個程序中沒有其他變量。這只是我擁有的一切。 – user2030677

1

的答案malloc(0)/free()電話不崩潰,你可以在這裏找到:

zero size malloc

關於*ptr = 9,就像溢出的緩衝區(如malloc'ing 10個字節,訪問11日) ,你正在寫給你不擁有的記憶,而這樣做是在尋找麻煩。在這個特定的實現中,malloc(0)恰好返回一個指針而不是NULL。

底線,即使它似乎在一個簡單的情況下工作是錯誤的。

+0

如果我已經完成了'malloc(1)'而不是'malloc(0)'特殊情況怎麼辦? – user2030677

+0

它仍然是錯誤的,因爲'int'可能是4個字節長。它似乎也可以工作,因爲系統可能會分配一整個字。 – imreal

+0

什麼意思是「整個單詞」? – user2030677

0

一些內存分配器具有「最小可分配大小」的概念。因此,即使你傳遞零,這將返回指向字大小內存的指針,例如。你需要檢查你的系統分配器文檔。但是如果它返回的是指向某個內存的指針,那麼依賴它是不對的,因爲指針只能傳遞給realloc()或free()。

2

一件事,你的編譯器可能被這兩條線看背靠背和優化他們:

*ptr = 9; 
printf("%i", *ptr); 

有了這樣一個簡單的程序,你的編譯器可能實際上被優化掉整個內存分配/釋放週期並使用常數代替。你的程序的編譯器優化的版本會看起來更像是簡單的:

printf("9"); 

只有這樣,才能告訴我們,如果這確實是正在發生的事情是檢查你的編譯器生成的程序集。如果您想了解C的工作方式,我建議您在構建代碼時明確禁用所有編譯器優化。

關於您的特定malloc用法,請記住,如果分配失敗,您將返回NULL指針。總是檢查返回值malloc之前,你使用它的任何東西。盲目取消引用是破壞程序的好方法。

尼克貼出的鏈接爲什麼malloc(0)似乎可以正常工作提供了一個很好的解釋(注意「作品」和「似乎有效」之間的重大區別)。總結那裏的信息,malloc(0)被允許返回NULL或指針。如果它返回一個指針,則明確禁止將它用於除free()以外的其他任何內容。如果你嘗試使用這樣一個指針,你調用未定義的行爲,並且沒有辦法告訴結果會發生什麼。它看起來可能適合你,但是這樣做可能會覆蓋屬於另一個程序的內存並破壞它們的內存空間。簡而言之:沒有什麼好的事情可以發生,所以請留下指針,不要浪費時間與malloc(0)

+0

編譯器可以優化'* ptr = 9; printf(「%i」,* ptr);'to'printf(「%i」,9);'但它肯定不會優化它到'printf(「9」);編譯器不會「知道」printf()什麼;它不能優化掉一個函數的整個參數! – phonetagger

+1

許多編譯器都知道* printf是什麼,並驗證格式字符串與傳遞給函數的值是否一致。 –

+0

'printf'有很多與之相關的開銷,所以編譯器儘可能優化它並不罕見。如果格式字符串和所有參數都是編譯時常量,那麼編譯器將其轉換爲預格式化的字符串(我曾多次看到它)是合理的。 – bta

2

很多很好的答案在這裏。但這絕對是未定義的行爲。有些人聲稱,未定義的行爲意味着紫龍可能會飛出你的電腦或類似的東西......這可能有一些背後的歷史背後,我失蹤了,但我保證紫龍不會出現未定義的行爲將是什麼。首先,讓我提一下,在沒有MMU的情況下,在沒有虛擬內存的系統上,程序將直接訪問系統內存的所有,而不管其地址如何。在這樣的系統中,malloc()僅僅是幫助你以有序的方式分割內存塊的人;系統實際上不能強制您僅使用malloc()給您的地址。在具有虛擬內存的系統上,情況是略微不同......好吧,好吧,很多不同。但是在您的程序中,程序中的任何代碼都可以訪問通過MMU映射到真實物理內存的虛擬地址空間的任何部分。無論您是從malloc()獲取地址,還是您調用rand(),並且碰巧得到落在程序映射區域中的地址,都無關緊要。如果它被映射並且沒有標記爲只執行,則可以讀取它。如果它沒有標記爲只讀,您也可以編寫它。是。即使你沒有從malloc()得到它。

讓我們考慮爲malloc(0)未定義行爲的可能性:

  • malloc(0)返回null。

好的,這很簡單。在大多數計算機中確實存在一個物理地址0x00000000,甚至虛擬地址0x00000000在全部進程中,但操作系統有意不將任何內存映射到該地址,以便它可以捕獲空指針訪問。整個頁面(通常爲4KB)完全沒有映射,可能甚至超過4KB。因此,如果您嘗試通過空指針讀取或寫入數據,即使偏移了它,也會觸發這些虛擬內存頁,這些虛擬內存頁甚至沒有映射,並且MMU將拋出異常(硬件異常,或中斷)操作系統捕獲,並且它聲明瞭SIGSEGV(在Linux/Unix上)或非法訪問(在Windows上)。

  • malloc(0)將有效地址返回到先前未分配的最小可分配單元的內存。

有了這個,你實際上得到了一個真正的記憶,你可以合法地稱你自己的,有一些你不知道的大小。你真的不應該在那裏寫任何東西(也可能沒有閱讀),因爲你不知道它有多大,並且就此而言,你不知道這是否是你遇到的特定情況(參見以下內容例)。如果是這樣的話,你給出的內存塊幾乎保證至少有4個字節,可能是8個字節或者甚至更大;這一切都取決於實現的最小可分配單元的大小。

  • malloc(0)有意返回NULL以外 存儲器未映射的頁的地址。

這可能是用於實現一個很好的選擇,因爲這將允許您或系統來追蹤&對在一起的malloc()與相應的自由()調用調用,但在本質上,這是一樣的回空值。如果你試圖通過這個指針訪問(讀/寫),你會崩潰(SEGV或非法訪問)。

  • malloc(0)在記憶 一些其他頁面映射可以通過「別人」使用返回的地址。

我發現商業上可用的系統不太可能採用這種方式,因爲它只是簡單地隱藏錯誤,而不是儘快將它們帶出。但是如果是這樣的話,malloc()會返回一個指向內存中某個地方的指針,你不擁有。如果是這種情況,當然,你可以寫出所有你想要的東西,但是你會損壞其他代碼的內存,儘管它會在你的程序過程中成爲內存,所以你可以放心,至少不是將會跺腳另一個程序的內存。 (我聽到有人準備說:「但它是UB,所以在技術上它可能會跺腳其他程序的內存。是的,在某些環境中,就像嵌入式系統一樣,沒有現代商業操作系統會讓一個進程可以像訪問另一個進程的內存那樣簡單地調用malloc(0);事實上,無需通過操作系統爲您執行操作,就無法從一個進程訪問另一個進程的內存。)無論如何,回到現實......這是「未定義行爲」真正開始的地方:如果你正在寫「別人的記憶」(在你自己的程序的過程中),你會改變程序的行爲以難以預測的方式知道你的程序的結構和所有內容在內存中的位置,這是完全可以預測的,但是從一個系統到另一個系統,事情將在內存中進行佈置(在內存中出現不同的位置),因此對一個系統的影響不一定會與對另一個系統或不同時間在同一系統上的影響相同。

  • 最後....不,就是這樣。真的,真的只有這四種可能性。您可以爲上述最後兩項的 爭辯特殊子集點,但最終結果將是相同的。
相關問題