日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入浅出之动态内存(new,malloc深度分析)

發(fā)布時間:2024/9/27 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入浅出之动态内存(new,malloc深度分析) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

void GetMemory(char *p)

{

p = (char *)malloc(100);

}

void Test(void)

{

char *str = NULL;

GetMemory(str);??

strcpy(str, "hello world");

printf(str);

}

請問運行Test函數(shù)會有什么樣的結(jié)果?

答:程序崩潰。

因為GetMemory并不能傳遞動態(tài)內(nèi)存,

Test函數(shù)中的?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函數(shù)會有什么樣的結(jié)果?

答:可能是亂碼。

因為GetMemory返回的是指向“棧內(nèi)存”的指針,該指針的地址不是?NULL,但其原現(xiàn)的內(nèi)容已經(jīng)被清除,新內(nèi)容不可知。

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函數(shù)會有什么樣的結(jié)果?

答:

(1)能夠輸出hello

(2)內(nèi)存泄漏

void Test(void)

{

char *str = (char *) malloc(100);

????strcpy(str,?“hello”);

????free(str);????

????if(str != NULL)

????{

?????strcpy(str,?“world”);

printf(str);

}

}

請問運行Test函數(shù)會有什么樣的結(jié)果?

答:篡改動態(tài)內(nèi)存區(qū)的內(nèi)容,后果難以預(yù)料,非常危險。

因為free(str);之后,str成為野指針,

if(str != NULL)語句不起作用。

1.? 關(guān)鍵字、操作符與庫函數(shù)

  • 關(guān)鍵字是編譯器保留的文字,不能被用戶拿來重新聲明,像const, new, if等等
  • 操作符必須要有操作對象,操作符本質(zhì)上可以視為編譯器內(nèi)置的基礎(chǔ)的函數(shù)。操作符在c++中,可以被重載(除了部分例外,比如 . :: sizeof)。
  • 庫函數(shù)是編寫在編譯器頭文件庫里,要包含頭文件才能調(diào)用的封裝函數(shù)。

2.?自由存儲區(qū)與堆的區(qū)別

堆(heap)是C語言和操作系統(tǒng)的術(shù)語。堆是操作系統(tǒng)所維護(hù)的一塊特殊內(nèi)存,它提供了動態(tài)分配的功能,當(dāng)運行程序調(diào)用malloc()時就會從中分配,稍后調(diào)用free可把內(nèi)存交還。而自由存儲是C++中通過new和delete動態(tài)分配和釋放對象的抽象概念,通過new來申請的內(nèi)存區(qū)域可稱為自由存儲區(qū)。基本上,所有的C++編譯器默認(rèn)使用堆來實現(xiàn)自由存儲,也即是缺省的全局運算符new和delete也許會按照malloc和free的方式來被實現(xiàn),這時藉由new運算符分配的對象,說它在堆上也對,說它在自由存儲區(qū)上也正確。但程序員也可以通過重載操作符,改用其他內(nèi)存來實現(xiàn)自由存儲,例如全局變量做的對象池,這時自由存儲區(qū)就區(qū)別于堆了。我們所需要記住的就是:

堆是操作系統(tǒng)維護(hù)的一塊內(nèi)存,而自由存儲是C++中通過new與delete動態(tài)分配和釋放對象的抽象概念。堆與自由存儲區(qū)并不等價。?

3. 野指針

野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒有明確限制的)指針變量在定義時如果未初始化,其值是隨機(jī)的,指針變量的值是別的變量的地址,意味著指針指向了一個地址是不確定的變量,此時去解引用就是去訪問了一個不確定的地址,所以結(jié)果是不可知的。?

4. malloc和new區(qū)別

在C++中,申請動態(tài)內(nèi)存與釋放動態(tài)內(nèi)存用new/delete 與 malloc/free都可以,new/malloc申請動態(tài)內(nèi)存,操作系統(tǒng)無法自動回收,需要對應(yīng)的delete/free釋放空間。

  • malloc/free 是c語言中的庫函數(shù),需要頭文件支持;而new/delete 是c++中的關(guān)鍵字
  • 使用new操作符時編譯器會根據(jù)類型信息自行計算所需要的內(nèi)存,而malloc需要顯式指出所需內(nèi)存的尺寸
int *p = new int[2]; int *q = (int *)malloc(2*sizeof(int));
  • malloc內(nèi)存分配成功時,返回的是void*,需要轉(zhuǎn)換成所需要的類型

    new內(nèi)存分配成功時,返回的是對象類型的指針,類型嚴(yán)格與對象匹配,無須進(jìn)行類型轉(zhuǎn)換,故new是符合類型安全性的操作符

    free釋放內(nèi)存的時候需要的是void* 類型的參數(shù)

    delete釋放內(nèi)存的時候需要使用具體類型的指針

  • new操作符在分配失敗的時候會拋出bac_alloc異常

    malloc在分配內(nèi)存失敗時返回NULL

  • new操作符從自由存儲區(qū)(free store)上為對象動態(tài)分配內(nèi)存空間,允許重載new/delete操作符

    malloc函數(shù)從堆上動態(tài)分配內(nèi)存,malloc不允許被重載

  • new會先調(diào)用operator new函數(shù),申請足夠的內(nèi)存(通常底層使用malloc實現(xiàn))。然后調(diào)用類型的構(gòu)造函數(shù),初始化成員變量,最后返回自定義類型指針。delete先調(diào)用析構(gòu)函數(shù),然后調(diào)用operator delete函數(shù)釋放內(nèi)存(通常底層使用free實現(xiàn))。

    malloc/free是庫函數(shù),只能動態(tài)的申請和釋放內(nèi)存,無法強(qiáng)制要求其做自定義類型對象構(gòu)造和析構(gòu)工作。

5.?new()和new[]的區(qū)別

  • ? new()創(chuàng)建一個對象
  • ? new[]創(chuàng)建一個動態(tài)數(shù)組
  • 釋放方法也不一樣,new()對應(yīng)delete,new[]對應(yīng)delete[]
char *pc = new char('a'); //開辟一個內(nèi)存單元,并用括號里的初始化 char *pca = new char[15]; //開辟一個數(shù)組 釋放內(nèi)存的方法也不一樣: delete pc; delete []pc;

6.?delete和delete[]的區(qū)別

?new 分配的單個對象的內(nèi)存空間的時候用 delete,回收用 new[] 分配的一組對象的內(nèi)存空間的時候用 delete[],其實要分基本數(shù)據(jù)類型和自定義數(shù)據(jù)類型。

  • 基本數(shù)據(jù)類型

int *a = new int[10];...delete a; // 方式1delete [ ] a; //方式2

基本的數(shù)據(jù)類型對象沒有析構(gòu)函數(shù),并且new 在分配內(nèi)存時會記錄分配的空間大小,則delete時能正確釋放內(nèi)存,無需調(diào)用析構(gòu)函數(shù)釋放其余指針。因此兩種方式均可。

  • 自定義數(shù)據(jù)類型?
#include <iostream>; using namespace std;class T { public:T() { cout << "constructor" << endl; }~T() { cout << "destructor" << endl; } };int main() {const int NUM = 3;T* p1 = new T[NUM];cout << hex << p1 << endl; //輸出P1的地址// delete[] p1;delete p1;cout << endl;T* p2 = new T[NUM];cout << p2 << endl; //輸出P2的地址delete[] p2;return 0; }

從運行結(jié)果中我們可以看出,delete p1 在回收空間的過程中,只有 p1[0] 這個對象調(diào)用了析構(gòu)函數(shù),其它對象如 p1[1]、p1[2] 等都沒有調(diào)用自身的析構(gòu)函數(shù),這就是問題的癥結(jié)所在。如果用 delete[],則在回收空間之前所有對象都會首先調(diào)用自己的析構(gòu)函數(shù)。
????基本類型的對象沒有析構(gòu)函數(shù),所以回收基本類型組成的數(shù)組空間用 delete 和 delete[] 都是應(yīng)該可以的;但是對于類對象數(shù)組,只能用 delete[]。對于 new 的單個對象,只能用 delete 不能用 delete[] 回收空間。
????所以一個簡單的使用原則就是:new 和 delete、new[] 和 delete[] 對應(yīng)使用。?

?7. delete或者free之要將指針指為NULL

delete和free被調(diào)用后,內(nèi)存不會立即回收,指針也不會指向空,delete或free僅僅是告訴操作系統(tǒng),這一塊內(nèi)存被釋放了,可以用作其他用途。但是由于沒有重新對這塊內(nèi)存進(jìn)行寫操作,所以內(nèi)存中的變量數(shù)值并沒有發(fā)生變化,這時候就會出現(xiàn)野指針的情況。因此,釋放完內(nèi)存后,應(yīng)該把指針指向NULL
?

#include <iostream> using namespace std;int main() {int *p = new int;*p = 3;cout << *p << endl;cout << p << endl;delete p;cout << *p << endl;cout << p << endl;return 0; }

?delete之后,p指向的地址并沒有發(fā)生變化,而地址里面的內(nèi)容卻成了隨機(jī)數(shù),那么,我們可以得到下面這條結(jié)論

我們在刪除一個指針之后,編譯器只會釋放該指針?biāo)赶虻膬?nèi)存空間,而不會刪除這個指針本身。
此時p也就成為一個野指針

#include <iostream> using namespace std;int main() {int *p = new int;delete p;p = NULL;delete p; }

如果不加p=NULL;程序運行時就會掛掉,加上就不會了,對NULL空間多次釋放時沒有問題的,但是不是NULL就不可以,因為這段空間已經(jīng)釋放掉,不屬于本程序,不能夠取隨意的釋放。

8. 使用原則

  • 原則1: 優(yōu)先使用new,delete
  • 原則2: 要new和delete配對,new[]和delete [],malloc和free配對使用
  • 原則3: free和delte后指針一定賦予NULL,防止成為野指針

9.C++智能指針如何指向數(shù)組

智能指針在幫助C++程序員管理動態(tài)內(nèi)存方面可謂神兵利器,但是在有些情況下我們想要對數(shù)組進(jìn)行動態(tài)內(nèi)存管理就會發(fā)現(xiàn)一個問題 咦?shared_ptr 在默認(rèn)情況下是不能指向數(shù)組的,那是為什么呢。

原因是因為我們的 shared_ptr 默認(rèn)的刪除器是使用 Delete 對智能指針中的對象進(jìn)行刪除,而 delete 要求 new 時是單一指針 Delete時也應(yīng)該是指針 new時是數(shù)組 delete 也應(yīng)該用數(shù)組類型去delete

shared_ptr
所以我們?nèi)绻胱屛覀兊?share_ptr 去指向指針 我們只需要去使用一個可調(diào)用對象即可 在這種情況下比較常用的函數(shù)或者lambda表達(dá)式均可

bool del(int *p){delete [] p; }shared_ptr<int> shared(new int[100],del);//使用函數(shù)shared_ptr<int> ptr(new int[100], [](int *p){delete [] p;});//使用lambda表達(dá)式


因為智能指針沒有重載下標(biāo)運算符 意味著我們不能想數(shù)組那樣去使用這個指針 那怎么樣才可以使用呢

shared_ptr 有一個函數(shù)可以返回當(dāng)前智能指針的內(nèi)置指針的函數(shù) 就是成員函數(shù)get() 但是我們要注意當(dāng)智能指針指針已經(jīng)釋放內(nèi)存以后,get得到的指針就成了空懸指針
有一點需要注意 在我們得到get返回的指針以后,智能指針對象的引用計數(shù)其實并沒有增加

?get() const noexcept{ return _M_ptr; }


這是get的函數(shù)定義

但是如果我們delete從get得到的指針 并不會出現(xiàn)多次delete的錯誤,現(xiàn)在還不是很理解為什么

我們該如何使用從get中得到的指針呢 其實很簡單,就是我們一般的指針操作即可

? ? auto x = ptr.get();cout << *(x+i) << endl;

注意!!!
其實只是C++11 中不支持而已 C++17中已經(jīng)支持

unique_ptr
相比與shared_ptr unique_ptr對于動態(tài)數(shù)組的管理就輕松多了 我們只需要直接使用即可

unique_ptr<int[]>unique(new int[100]);


而且unique_ptr是重載了下標(biāo)運算符的,意味著我們可以方便把其當(dāng)數(shù)組一樣使用

Boost C++庫
著名的Boost庫其實是支持指向數(shù)組的,使用方法與unique_ptr差不多

Boost庫是什么?

類名為boost::shared_array,定義在<boost/shared_ptr.hpp>

boost::shared_array<int> arr(new int[100]);?


?

參考文獻(xiàn):

  • 關(guān)鍵字、操作符與庫函數(shù)01
  • 自由存儲區(qū)和堆的區(qū)別
  • new 和 malloc free 和 delete 的區(qū)別
  • C++中的delete和delete[ ]的區(qū)別
  • C++中delete和delete[]的區(qū)別 - charley_yang - 博客園
  • C/C++動態(tài)內(nèi)存管理malloc/new、free/delete的異同
  • delete一個指針之后,要記得設(shè)置為NULL
  • delete或者free之后為什么要將指針指為NULL
  • C++智能指針如何指向數(shù)組

  • 總結(jié)

    以上是生活随笔為你收集整理的深入浅出之动态内存(new,malloc深度分析)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。