使用delete釋放new[]的空間造成的錯誤分析


曲折探索后,這個問題算是水落石出。

我們都被告誡,new和delete,new[]和delete[]要成對出現。如果使用delete 釋放new[] 申請的空間會發什么?如下:

T* p = new T [1024];

....//do something

delete p;//會發生什么?

我先告訴你,如果T是一個base type(如,int、double、char),你會發現,程序依然正確運行,不僅如此,內存空間也被正確釋放。它的正確運行,讓很多人對delete[]和delete的區別的探索就此終止。也難怪在國內的博客中分析delete和delete[]的原理和delete 釋放new[]的文章少之又少,很多相關的文章關於這個問題一句帶過。

如果代碼是這樣的:

class A { public: int m; ~A() {} }; int main() { A* ptr = new A[1024]; delete ptr; return 0; }

你可以測試一下這段代碼,在VS和g++下都測試一下,看看都出現什么結果。這會增加你日后在程序調試bug的經驗(我已經遭遇一次)

 


要明白這個問題,我們要知道new和new[],delete和delete[]都干什么了些什么。

不用多說new和delete。

1.new封裝了C語言中的malloc 函數,申請一塊內存空間,如果T是用戶自定義的類類型,將使用T的構造函數初始化這塊內存空間上的對象。

2.delete封裝了C語言中的free 函數,如果T是用戶自定義的類類型,首先調用T的析構函數,再調用free釋放這個對象占用的內存空間。

new[]會更復雜一些:

T* ptr = new  T [1024]; delete[] ptr;

我們注意到,沒有傳入任何關於ptr指針的信息給delete[]操作符運算符(operator)。最初的C++編譯器版本是要求程序員傳入關於指針指向空間的大小的參數,而現代的編譯器雖然也接受,但多數情況會忽視它[1]!

既然沒有傳入參數,那編譯器一定是通過某種手段獲知ptr指向空間的大小。

是的,說某種手段,是因為有多種方法。在C++發展的最初,有一種關聯數組的方法:T* ptr = new[s]申請一段空間后,編譯器會在內存的某個地方固定的數組中存入ptr和ptr對應的空間大小s,下次在調用delete[]的時候,就會查找這個數組中的ptr對應的s。[2]

這個方法,現代的編譯器已經很少見了,因為它很慢。

現代的g++、vc、icpc都使用信息頭(additional information head block)的方法來管理。在new[]申請內存空間時,會多申請n個字節,這n個字節就是信息頭,位於我們ptr指針向低地址偏移n個字節的地方(block = ptr - n)。每次delete[]被調用時,編譯器就會去這個信息頭查找申請的內存信息。

所以,調用new[]得到的東西和調用malloc得到的東西並不相同!

現在,我們分析一下

A* ptr = new A [1024];

delete ptr;

這段代碼在delete的時候發生了什么:

首先,和delete任何時候一樣,編譯器會給delete指派A的析構函數,以函數指針的形式,delete會調用這個析構函數,作用於ptr指向的這個對象上,顯然ptr之后的1023個對象不會調用析構函數,因為我們使用的是delete而不是delete[]。然后,delete執行free函數,釋放申請的空間。

但是!我們new[]中調用malloc得到的指針並不是從ptr開始的,我們的程序找不到malloc分配的ptr指針,就會崩潰![3]

而為什么base type不會有問題呢?程序運行得好好的。是的,都是編譯器的原因。編譯器檢查到,如果是base tyep,new[]操作就會省去前面的信息頭,后面發生delete釋放new[]的時候,free調用的ptr和malloc調用的ptr也就一致了。當然,這不一定,也有可能某些編譯器,在某些時候,即便是base type它也要加上head block呢?!

所以,如果new和delete,new[]和delete[]不配對,發生什么,未定義!

 

看到這里,相信你已經明白了delete[]為什么要對應new[]了,而delete也不能和delete[]混用的原因。

 

有什么問題歡迎留言,交流。

 


[1][2]《深入探索C++對象模型》

[3]http://stackoverflow.com/questions/8940090/is-it-safe-on-linux-to-mix-new-and-delete    ”LiKao“ 的回答

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM