2011-02-24 78 views
0

我開始注意到,有時當我在某些程序中釋放內存時,它們會莫名其妙地崩潰。我開始縮小的罪魁禍首,並拿出那說明我有困難的理解時的例子:C++免費()更改其他內存

#include <iostream> 
#include <stdlib.h> 

using namespace std; 

int main() { 
char *tmp = (char*)malloc(16); 
char *tmp2 = (char*)malloc(16); 

long address = reinterpret_cast<long>(tmp); 
long address2 = reinterpret_cast<long>(tmp2); 
cout << "tmp = " << address << "\n"; 
cout << "tmp2 = " << address2 << "\n"; 

memset(tmp, 1, 16); 
memset(tmp2, 1, 16); 

char startBytes[4] = {0}; 
char endBytes[4] = {0}; 

memcpy(startBytes, tmp - 4, 4); 
memcpy(endBytes, tmp + 16, 4); 
cout << "Start: " << static_cast<int>(startBytes[0]) << " " << static_cast<int>(startBytes[1]) << " " << static_cast<int>(startBytes[2]) << " " << static_cast<int>(startBytes[3]) << "\n"; 
cout << "End: " << static_cast<int>(endBytes[0]) << " " << static_cast<int>(endBytes[1]) << " " << static_cast<int>(endBytes[2]) << " " << static_cast<int>(endBytes[3]) << "\n"; 
cout << "---------------\n"; 

free(tmp); 

memcpy(startBytes, tmp - 4, 4); 
memcpy(endBytes, tmp + 16, 4); 
cout << "Start: " << static_cast<int>(startBytes[0]) << " " << static_cast<int>(startBytes[1]) << " " << static_cast<int>(startBytes[2]) << " " << static_cast<int>(startBytes[3]) << "\n"; 
cout << "End: " << static_cast<int>(endBytes[0]) << " " << static_cast<int>(endBytes[1]) << " " << static_cast<int>(endBytes[2]) << " " << static_cast<int>(endBytes[3]) << "\n"; 

free(tmp2); 

return 0; 
} 

這裏是我看到的輸出:我使用

tmp = 8795380 
tmp2 = 8795400 
Start: 16 0 0 0 
End: 16 0 0 0 
--------------- 
Start: 17 0 0 0 
End: 18 0 0 0 

Borland的免費編譯器。我知道,我正在看的頭字節是特定於實現的,像「reinterpret_cast」這樣的東西是不好的做法。我只想找到答案的問題是:爲什麼「結束」的第一個字節從16更改爲18?

被認爲「結束」的4個字節是tmp之後的16個字節,它們是tmp2之前的4個字節。它們是tmp2的頭文件 - 爲什麼在tmp上調用free()會影響內存中的這個地方?

我嘗試過使用new []和delete []創建/刪除tmp和tmp2的相同示例,並且會出現相同的結果。

任何信息或幫助理解爲什麼這個特定的地方在記憶中受到影響將不勝感激。

+5

不相關,但C++程序員應該使用new和delete(或new []和delete []),而不是malloc和free。 – 2011-02-24 22:32:10

+1

更普遍的是,如果你使用'new []'和'delete []',你可能真的需要'std :: vector'。 – birryree 2011-02-24 22:35:19

+0

我不能再給J.N.的+1評論,但如果我能的話,我會! – corsiKa 2011-02-24 22:35:51

回答

5

你將不得不問你的libc實施它爲什麼改變。無論如何,它爲什麼重要?這是libc尚未分配給您的內存區域,可能用於維護自己的數據結構或一致性檢查,或者可能根本沒有使用。

+0

這只是一個出於好奇而提出的問題,但無論你是否正確 - 理由並不重要,因爲它不是我使用的記憶。 – 2011-02-24 22:56:48

+0

@Jason Terranova,這不是諷刺,任何理由都將取決於實施。例如,如果你使用glibc在Linux上,請查看你的glibc版本的源代碼並深入研究。如果你不知道你的libc實現,那麼你的問題就不能得到回答,一般來說:「X未定義當我做X時,爲什麼我會看到Y?「 – rlibby 2011-02-24 23:37:08

0

從堆中刪除已分配元素的操作可能會修改其他堆節點,或者實現保留一個或多個字節的頭以用作先前分配的保護字節。

2

基本上你正在尋找你沒有分配的記憶。你不能假設你所請求的內存以外的內存會發生什麼(即你分配的16個字節)。沒有任何異常發生。

運行時和編譯器可以自由地做他們想做的事情,所以你不應該在你的程序中使用它們。運行時可能會更改這些字節的值以跟蹤其內部狀態。 釋放內存不太可能導致程序崩潰。另一方面,訪問你已經解除分配的內存就像是你的樣本,這是很大的編程錯誤,很可能會這樣做。

一個避免這種情況的好方法是將任意指針設置爲NULL。這樣做會在訪問釋放的變量時強制程序崩潰。

0

內存管理器必須記住,例如分配的內存塊的大小是多少,malloc。有不同的方法,但可能最簡單的方法是分配比調用中請求的大小多4個字節,並在指針返回給調用者之前存儲大小值。

然後,free的實現可以從傳遞的指針中減去4個字節,以獲得指向存儲大小的指針,然後可以將塊(例如)鏈接到該大小的可用可重用塊列表(可以再次使用這4個字節來存儲到下一個塊的鏈接)。

你不應該改變甚至在分配區域之前/之後查看字節。訪問的結果,即使只是爲了閱讀,你沒有分配的內存也是未定義的行爲(是的,你真的可以通過閱讀未分配的內存來真正導致程序崩潰或行爲異常)。