2011-11-06 126 views
71

也許它是從平臺到平臺不同的,但是爲什麼malloc在gcc中將值初始化爲0?

當我使用gcc編譯並運行下面的代碼時,我每次都在我的ubuntu 11.10中得到0。

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

int main() 
{ 
    double *a = (double*) malloc(sizeof(double)*100) 
    printf("%f", *a); 
} 

爲什麼malloc的行爲就像這樣,即使有calloc?

這是否意味着即使您不希望它有時會將值初始化爲0,也會產生不必要的性能開銷?編輯:哦,我以前的例子不是開始,但恰巧使用「新鮮」塊。

我正是一直在尋找是爲什麼當它分配一個大塊它初始化:

int main() 
{ 
    int *a = (int*) malloc(sizeof(int)*200000); 
    a[10] = 3; 
    printf("%d", *(a+10)); 

    free(a); 

    a = (double*) malloc(sizeof(double)*200000); 
    printf("%d", *(a+10)); 
} 

OUTPUT: 3 
     0 (initialized) 

但感謝指出有mallocing當安全理由! (從來沒有想過)。當分配新塊或大塊時,必須將其初始化爲零。

+12

爲了更真實的測試,你嘗試過分配,釋放,然後重新分配(可能每個多次重複)?僅僅因爲malloc第一次返回零初始化的內存並不意味着你可以一般地指望它。 – user786653

+1

它也可能是內存被操作系統設置爲0,或者'malloc'與它無關。 –

+0

[出於安全原因,不要在C中輸入'malloc'的結果(http://stackoverflow.com/q/605845/995714) –

回答

157

答案很簡單:

它不,它只是發生在你的情況下爲零。
(也測試用例不顯示數據是零,僅顯示瞭如果一個元素是零。)


龍答:

當你調用malloc(),兩個一事情將發生:

  1. 它回收先前分配的內存,並從同一進程中釋放。
  2. 它從操作系統請求新的頁面。

在第一種情況下,內存將包含先前分配剩餘的數據。所以它不會是零。這是執行小分配時的常見情況。

在第二種情況下,內存將來自操作系統。當程序運行內存不足或者請求非常大的分配時,會發生這種情況。 (如你的例子)

這裏的問題:來自操作系統的內存將歸零爲安全的原因。*

當操作系統給你的內存,它可能是從一個不同的進程中解脫出來。所以內存可能包含密碼等敏感信息。因此,爲了防止您讀取這些數據,操作系統在將它提供給您之前將其清零。

*我注意到,C標準沒有提到這一點。這完全是一種操作系統行爲。因此,這種調零可能會或可能不會出現在不擔心安全問題的系統上。


爲了讓更多的性能背景這一點:

由於@R。在評論中提到,這個調零是爲什麼你應該總是use calloc() instead of malloc() + memset()calloc()可以利用這個事實來避免單獨的memset()


另一方面,這種調零有時是性能瓶頸。在某些數值應用程序(例如out-of-place FFT)中,您需要分配大量的暫存內存。用它來執行任何算法,然後釋放它。

在這些情況下,調零是不必要的,相當於純開銷。

最極端的例子我所看到的是用於與48 GB臨時緩衝區一個70第二操作20秒回零開銷。 (大約30%的開銷) (授予:機器也有一個缺少的存儲器帶寬。)

顯而易見的解決方案是簡單地手動重新使用存儲器。但是這通常需要突破已建立的界面。 (尤其是如果它是庫例程的一部分)

+18

+1。 –

+14

但是你仍然不能指望它是零,除非你自己這樣做(或者使用'calloc',這是從OS獲得內存後爲你做的)。 –

+0

感謝您的回答。 mallocing時從未想過會有安全問題! – SHH

0

你知道它肯定被初始化嗎? malloc()返回的區域是否可能在開始時經常有0?

2

該標準沒有規定malloc()應該將這些值初始化爲零。它只是發生在你的平臺上,它可能被設置爲零,或者在你讀取該值的特定時刻可能爲零。

2

您的代碼沒有證明malloc將其內存初始化爲0.這可以在程序啓動之前由操作系統完成。爲了看到這種情況,向內存寫入一個不同的值,釋放它,然後再次調用malloc。你可能會得到相同的地址,但你必須檢查。如果是這樣,你可以看看它包含什麼。讓我們知道!

20

操作系統通常會明確記憶猶新頁面發送給你的過程,因此不能看的舊進程的數據。這意味着你第一次初始化一個變量(或者malloc的東西)時,它通常會是零,但是如果你重複使用了這個內存(比如釋放它和再次malloc),那麼所有的賭注都是關閉的。

這種不一致就是爲什麼未初始化的變量很難找到這樣的錯誤。


對於不需要的性能方面的開銷,避免不確定的行爲可能是更重要的。無論在這種情況下可以獲得的小性能提升都不會彌補難以找到的錯誤,如果有人稍微修改代碼(破壞以前的假設)或將其移植到另一個系統(假設可能無效首先)。

+3

+1 ...不知道「可能」在粗體文本中是必需的;-) – 2011-11-06 19:25:42

15

爲什麼你認爲malloc()初始化爲零?只是碰巧第一次撥打電話malloc()導致致電sbrkmmap系統調用,系統調用從OS分配一頁內存。由於安全原因,操作系統必須提供零初始化內存(否則,其他進程的數據可見!)。所以你可能會認爲 - 操作系統浪費時間將頁面歸零。但不是!在Linux中,有一個特殊的系統範圍的單獨頁面被稱爲「零頁面」,該頁面將被映射爲寫入時複製,這意味着只有當你真正在該頁面上寫入時,操作系統纔會分配另一頁面,初始化它。所以我希望這能回答你關於表現的問題。內存分頁模型允許通過支持同一頁面的多重映射能力以及在第一次寫入時處理事件的能力來緩存內存。

如果調用free(),該glibc分配器將區域返回到其空閒列表,當malloc()再次調用,你可能會得到同樣的區域,但髒以前的數據。最後,free()可能會通過再次調用系統調用將內存返回到操作系統。

注意,glibc手冊頁malloc()嚴格的說,內存不被清除,因此通過對API的「契約」,你不能想當然地認爲它得到清除。這裏是原始摘錄:

malloc()分配大小字節並返回一個指向分配內存的指針。
內存未被清除。如果大小爲0,則malloc()返回NULL, 或稍後可以成功傳遞給free()的唯一指針值。

如果您願意,如果您擔心性能或其他副作用,可以閱讀有關該文檔的更多信息。

0

從不有史以來指望任何編譯器生成的代碼將初始化內存任何東西。 malloc只是返回一個指向n個字節的內存某處它可能甚至會交換。

如果內存的內容非常重要,請自行初始化。

+4

除了在語言保證它將被初始化的情況下。沒有顯式初始化的靜態對象被隱式地初始化爲零。 –

2

gnu.org

非常大的塊(比頁面更大)被分配使用mmap(匿名或通過的/ dev /零)此實現。

+0

儘管OP在小步驟中正在運行。你發現的這個參考文獻是否也有關於此的內容? – hugomg

13

我修改了您的示例以包含2個相同的分配。現在很容易看到malloc不會初始化內存爲零。

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

int main(void) 
{ 
    { 
     double *a = malloc(sizeof(double)*100); 
     *a = 100; 
     printf("%f\n", *a); 
     free(a); 
    } 
    { 
     double *a = malloc(sizeof(double)*100); 
     printf("%f\n", *a); 
     free(a); 
    } 

    return 0; 
} 

輸出用gcc 4.3.4

100.000000 
100.000000 
+0

我試過你所做的,如果我只分配100個字節,那麼即使指針指向同一地址,該地址的值也不同。如果我分配400字節或更多,則指針值和內存中的值都是相同的。你認爲可能是什麼原因? –