深入浅出之动态内存(new,malloc深度分析)
| void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str);?? strcpy(str, "hello world"); printf(str); } 請問運行Test函數會有什么樣的結果? 答:程序崩潰。 因為GetMemory并不能傳遞動態內存, Test函數中的?str一直都是?NULL。 strcpy(str, "hello world");將使程序崩潰。 | char *GetMemory(void) {?? char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory();??? printf(str); } 請問運行Test函數會有什么樣的結果? 答:可能是亂碼。 因為GetMemory返回的是指向“棧內存”的指針,該指針的地址不是?NULL,但其原現的內容已經被清除,新內容不可知。 |
| void GetMemory2(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello");?? printf(str);??? } 請問運行Test函數會有什么樣的結果? 答: (1)能夠輸出hello (2)內存泄漏 | void Test(void) { char *str = (char *) malloc(100); ????strcpy(str,?“hello”); ????free(str);???? ????if(str != NULL) ????{ ?????strcpy(str,?“world”); printf(str); } } 請問運行Test函數會有什么樣的結果? 答:篡改動態內存區的內容,后果難以預料,非常危險。 因為free(str);之后,str成為野指針, if(str != NULL)語句不起作用。 |
1.? 關鍵字、操作符與庫函數
- 關鍵字是編譯器保留的文字,不能被用戶拿來重新聲明,像const, new, if等等
- 操作符必須要有操作對象,操作符本質上可以視為編譯器內置的基礎的函數。操作符在c++中,可以被重載(除了部分例外,比如 . :: sizeof)。
- 庫函數是編寫在編譯器頭文件庫里,要包含頭文件才能調用的封裝函數。
2.?自由存儲區與堆的區別
堆(heap)是C語言和操作系統的術語。堆是操作系統所維護的一塊特殊內存,它提供了動態分配的功能,當運行程序調用malloc()時就會從中分配,稍后調用free可把內存交還。而自由存儲是C++中通過new和delete動態分配和釋放對象的抽象概念,通過new來申請的內存區域可稱為自由存儲區。基本上,所有的C++編譯器默認使用堆來實現自由存儲,也即是缺省的全局運算符new和delete也許會按照malloc和free的方式來被實現,這時藉由new運算符分配的對象,說它在堆上也對,說它在自由存儲區上也正確。但程序員也可以通過重載操作符,改用其他內存來實現自由存儲,例如全局變量做的對象池,這時自由存儲區就區別于堆了。我們所需要記住的就是:
堆是操作系統維護的一塊內存,而自由存儲是C++中通過new與delete動態分配和釋放對象的抽象概念。堆與自由存儲區并不等價。?
3. 野指針
野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)指針變量在定義時如果未初始化,其值是隨機的,指針變量的值是別的變量的地址,意味著指針指向了一個地址是不確定的變量,此時去解引用就是去訪問了一個不確定的地址,所以結果是不可知的。?
4. malloc和new區別
在C++中,申請動態內存與釋放動態內存用new/delete 與 malloc/free都可以,new/malloc申請動態內存,操作系統無法自動回收,需要對應的delete/free釋放空間。
- malloc/free 是c語言中的庫函數,需要頭文件支持;而new/delete 是c++中的關鍵字
- 使用new操作符時編譯器會根據類型信息自行計算所需要的內存,而malloc需要顯式指出所需內存的尺寸
-
malloc內存分配成功時,返回的是void*,需要轉換成所需要的類型
new內存分配成功時,返回的是對象類型的指針,類型嚴格與對象匹配,無須進行類型轉換,故new是符合類型安全性的操作符
free釋放內存的時候需要的是void* 類型的參數
delete釋放內存的時候需要使用具體類型的指針
-
new操作符在分配失敗的時候會拋出bac_alloc異常
malloc在分配內存失敗時返回NULL
-
new操作符從自由存儲區(free store)上為對象動態分配內存空間,允許重載new/delete操作符
malloc函數從堆上動態分配內存,malloc不允許被重載
-
new會先調用operator new函數,申請足夠的內存(通常底層使用malloc實現)。然后調用類型的構造函數,初始化成員變量,最后返回自定義類型指針。delete先調用析構函數,然后調用operator delete函數釋放內存(通常底層使用free實現)。
malloc/free是庫函數,只能動態的申請和釋放內存,無法強制要求其做自定義類型對象構造和析構工作。
5.?new()和new[]的區別
- ? new()創建一個對象
- ? new[]創建一個動態數組
- 釋放方法也不一樣,new()對應delete,new[]對應delete[]
6.?delete和delete[]的區別
?new 分配的單個對象的內存空間的時候用 delete,回收用 new[] 分配的一組對象的內存空間的時候用 delete[],其實要分基本數據類型和自定義數據類型。
-
基本數據類型
基本的數據類型對象沒有析構函數,并且new 在分配內存時會記錄分配的空間大小,則delete時能正確釋放內存,無需調用析構函數釋放其余指針。因此兩種方式均可。
- 自定義數據類型?
從運行結果中我們可以看出,delete p1 在回收空間的過程中,只有 p1[0] 這個對象調用了析構函數,其它對象如 p1[1]、p1[2] 等都沒有調用自身的析構函數,這就是問題的癥結所在。如果用 delete[],則在回收空間之前所有對象都會首先調用自己的析構函數。
????基本類型的對象沒有析構函數,所以回收基本類型組成的數組空間用 delete 和 delete[] 都是應該可以的;但是對于類對象數組,只能用 delete[]。對于 new 的單個對象,只能用 delete 不能用 delete[] 回收空間。
????所以一個簡單的使用原則就是:new 和 delete、new[] 和 delete[] 對應使用。?
?7. delete或者free之要將指針指為NULL
delete和free被調用后,內存不會立即回收,指針也不會指向空,delete或free僅僅是告訴操作系統,這一塊內存被釋放了,可以用作其他用途。但是由于沒有重新對這塊內存進行寫操作,所以內存中的變量數值并沒有發生變化,這時候就會出現野指針的情況。因此,釋放完內存后,應該把指針指向NULL
?
?delete之后,p指向的地址并沒有發生變化,而地址里面的內容卻成了隨機數,那么,我們可以得到下面這條結論
我們在刪除一個指針之后,編譯器只會釋放該指針所指向的內存空間,而不會刪除這個指針本身。
此時p也就成為一個野指針。
如果不加p=NULL;程序運行時就會掛掉,加上就不會了,對NULL空間多次釋放時沒有問題的,但是不是NULL就不可以,因為這段空間已經釋放掉,不屬于本程序,不能夠取隨意的釋放。
8. 使用原則
- 原則1: 優先使用new,delete
- 原則2: 要new和delete配對,new[]和delete [],malloc和free配對使用
- 原則3: free和delte后指針一定賦予NULL,防止成為野指針
9.C++智能指針如何指向數組
智能指針在幫助C++程序員管理動態內存方面可謂神兵利器,但是在有些情況下我們想要對數組進行動態內存管理就會發現一個問題 咦?shared_ptr 在默認情況下是不能指向數組的,那是為什么呢。
原因是因為我們的 shared_ptr 默認的刪除器是使用 Delete 對智能指針中的對象進行刪除,而 delete 要求 new 時是單一指針 Delete時也應該是指針 new時是數組 delete 也應該用數組類型去delete
shared_ptr
所以我們如果想讓我們的 share_ptr 去指向指針 我們只需要去使用一個可調用對象即可 在這種情況下比較常用的函數或者lambda表達式均可
因為智能指針沒有重載下標運算符 意味著我們不能想數組那樣去使用這個指針 那怎么樣才可以使用呢
shared_ptr 有一個函數可以返回當前智能指針的內置指針的函數 就是成員函數get() 但是我們要注意當智能指針指針已經釋放內存以后,get得到的指針就成了空懸指針
有一點需要注意 在我們得到get返回的指針以后,智能指針對象的引用計數其實并沒有增加
這是get的函數定義
但是如果我們delete從get得到的指針 并不會出現多次delete的錯誤,現在還不是很理解為什么
我們該如何使用從get中得到的指針呢 其實很簡單,就是我們一般的指針操作即可
? ? auto x = ptr.get();cout << *(x+i) << endl;注意!!!
其實只是C++11 中不支持而已 C++17中已經支持
unique_ptr
相比與shared_ptr unique_ptr對于動態數組的管理就輕松多了 我們只需要直接使用即可
而且unique_ptr是重載了下標運算符的,意味著我們可以方便把其當數組一樣使用
Boost C++庫
著名的Boost庫其實是支持指向數組的,使用方法與unique_ptr差不多
Boost庫是什么?
類名為boost::shared_array,定義在<boost/shared_ptr.hpp>
boost::shared_array<int> arr(new int[100]);?
?
參考文獻:
C++智能指針如何指向數組
總結
以上是生活随笔為你收集整理的深入浅出之动态内存(new,malloc深度分析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 视场角计算原理
- 下一篇: 深入浅出之函数的参数传递方式