国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【c/c++】深入探秘:C++內(nèi)存管理的機制

這篇具有很好參考價值的文章主要介紹了【c/c++】深入探秘:C++內(nèi)存管理的機制。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

??個人主頁Quitecoder

??專欄c++筆記倉

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

朋友們大家好,本篇文章我們詳細(xì)講解c++中的動態(tài)內(nèi)存管理

1.C/C++內(nèi)存分布

我們來看內(nèi)存區(qū)域劃分

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

數(shù)據(jù)段就是我們所說的全局變量,代碼段是我們所說的常量區(qū),我們需要重點關(guān)注的是堆區(qū),這部分是由我們自己控制的

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
  static int staticVar = 1;
  int localVar = 1;
  int num1[10] = { 1, 2, 3, 4 };
  char char2[] = "abcd";
  const char* pChar3 = "abcd";
  int* ptr1 = (int*)malloc(sizeof(int) * 4);
  int* ptr2 = (int*)calloc(4, sizeof(int));
  int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
  free(ptr1);
  free(ptr3);
}
 選擇題:
   選項: A.棧  B.堆  C.數(shù)據(jù)段(靜態(tài)區(qū))  D.代碼段(常量區(qū))
   globalVar在哪里?__1__   staticGlobalVar在哪里?__2__
   staticVar在哪里?__3__   localVar在哪里?__4__
   num1 在哪里?__5__
   
   char2在哪里?__6__   *char2在哪里?_7__
   pChar3在哪里?__8__      *pChar3在哪里?__9__
   ptr1在哪里?_10___        *ptr1在哪里?__11__

我們來依次討論:

  1. globalVar 是全局變量,不是靜態(tài)的,所以它存儲在數(shù)據(jù)段(靜態(tài)區(qū))
  2. staticGlobalVar 也是全局變量,但它是靜態(tài)的,因此它同樣存儲在數(shù)據(jù)段(靜態(tài)區(qū))
  3. staticVar 是函數(shù)內(nèi)的靜態(tài)變量,所以它存儲在數(shù)據(jù)段(靜態(tài)區(qū)),因為它的生命周期貫穿程序的整個執(zhí)行期
  4. localVar 是局部變量,存儲在棧上
  5. num1 是局部變量,它是數(shù)組,存儲在棧上
  6. char2 是局部變量,它是數(shù)組首元素的地址,存儲在棧上
  7. *char2(即char2數(shù)組的內(nèi)容)存儲在棧上,因為char2本身就在棧上
  8. pChar3 是局部指針變量,存儲在
  9. *pChar3 指向的內(nèi)容(即字符串"abcd")存儲在代碼段(常量區(qū))
  10. ptr1 是局部指針變量,存儲在
  11. *ptr1 指向的內(nèi)容(即通過malloc分配的內(nèi)存)存儲在
  • *char2(局部字符數(shù)組)
    當(dāng)你聲明一個局部字符數(shù)組并用一個字符串字面量初始化它,如char char2[] = "abcd";時,編譯器在棧上為數(shù)組分配內(nèi)存,然后將字符串字面量的內(nèi)容(包括結(jié)尾的\0字符)復(fù)制到這塊內(nèi)存中。因此,char2和它的內(nèi)容(*char2指向的內(nèi)容)都存儲在棧上

  • *pChar3(字符串字面量指針)
    另一方面,當(dāng)你使用指針指向一個字符串字面量,如const char* pChar3 = "abcd";時,這個字符串字面量存儲在程序的只讀數(shù)據(jù)段(或稱為代碼段、常量區(qū))中。pChar3本身作為一個局部指針變量存儲在棧上,但它指向的字符串(“abcd”)實際上存儲在常量區(qū)。這是因為字符串字面量被視為常量數(shù)據(jù),編譯器會將它們放在程序的常量區(qū)域內(nèi),這個區(qū)域通常是只讀的,以防止程序意外修改它的內(nèi)容。因此,盡管pChar3是一個指針,存儲在棧上,但它指向的字符串內(nèi)容存儲在常量區(qū)

總結(jié)

  • *char2不在常量區(qū),因為char2是局部字符數(shù)組,其內(nèi)容直接存儲在棧上。
  • *pChar3在常量區(qū),因為它指向的是一個字符串字面量,字符串字面量被存儲在程序的常量區(qū)域,這部分內(nèi)存是只讀的。

當(dāng)我們討論變量存儲在哪里時,通常涉及到幾個關(guān)鍵區(qū)域:棧(Stack)、堆(Heap)、數(shù)據(jù)段(Data Segment,又稱靜態(tài)區(qū))、和代碼段(Code Segment,又稱常量區(qū))。每種類型的變量根據(jù)其特性和聲明周期被存儲在這些區(qū)域中的相應(yīng)位置

  • 是用于存儲局部變量、函數(shù)參數(shù)等的內(nèi)存區(qū)域。當(dāng)一個函數(shù)被調(diào)用時,其局部變量和一些書keeping信息被推入棧中;當(dāng)函數(shù)執(zhí)行完成,這些信息被從棧上彈出。棧是自動管理的,開發(fā)者無需手動分配或釋放內(nèi)存。

  • 是用于動態(tài)內(nèi)存分配的內(nèi)存區(qū)域。不同于棧,開發(fā)者需要顯式地從堆上分配內(nèi)存(如使用mallocnew),并在不再需要時釋放這些內(nèi)存(如使用freedelete)。

  • 數(shù)據(jù)段,又稱為靜態(tài)區(qū),用于存儲全局變量、靜態(tài)變量等。這些變量的生命周期貫穿整個程序執(zhí)行期,因此它們被存儲在一個特定的、持久的內(nèi)存區(qū)域中。

  • 代碼段,又稱為常量區(qū),用于存儲程序的執(zhí)行代碼和常量數(shù)據(jù),如字符串字面量。這部分內(nèi)存是只讀的,用來保證程序代碼的安全性

2.C語言中動態(tài)內(nèi)存管理方式:malloc/calloc/realloc/free

在C語言中,動態(tài)內(nèi)存管理是通過一組標(biāo)準(zhǔn)庫函數(shù)完成的,包括malloc, calloc, realloc, 和 free。這些函數(shù)允許程序在運行時動態(tài)地分配、調(diào)整和釋放堆內(nèi)存,這是對于管理變化的數(shù)據(jù)量和大小特別有用的能力。下面是這些函數(shù)的基本用法和它們之間的區(qū)別:

malloc

  • 用法void* malloc(size_t size);
  • 功能:分配指定字節(jié)數(shù)的未初始化內(nèi)存。它返回一個指向分配的內(nèi)存的指針。如果分配失敗,返回NULL。
  • 示例int* ptr = (int*)malloc(sizeof(int) * 4); 這行代碼為4個整數(shù)分配了內(nèi)存

calloc

  • 用法void* calloc(size_t num, size_t size);
  • 功能:為指定數(shù)量的元素分配內(nèi)存,每個元素的大小也在參數(shù)中指定,并自動初始化所有位為0。如果分配失敗,返回NULL。
  • 示例int* ptr = (int*)calloc(4, sizeof(int)); 這行代碼為4個整數(shù)分配了內(nèi)存,并將它們初始化為0。

realloc

  • 用法void* realloc(void* ptr, size_t size);
  • 功能調(diào)整之前調(diào)用malloccalloc分配的內(nèi)存塊的大小。如果新的大小大于原始大小,可能會移動內(nèi)存塊到新的位置以提供足夠的連續(xù)空間。如果realloc的第一個參數(shù)是NULL,它的行為就像malloc。
  • 示例ptr = (int*)realloc(ptr, sizeof(int) * 8); 這行代碼將之前分配的內(nèi)存大小調(diào)整為8個整數(shù)的大小。

free

  • 用法void free(void* ptr);
  • 功能:釋放之前通過malloc, calloc, 或 realloc分配的內(nèi)存。一旦內(nèi)存被釋放,那塊內(nèi)存就不能再被訪問了。
  • 注意:嘗試釋放未經(jīng)分配的內(nèi)存塊或多次釋放同一個內(nèi)存塊是不安全的,可能導(dǎo)致未定義行為

注意

  • 在使用這些函數(shù)時,確保正確處理內(nèi)存分配失敗的情況,并在內(nèi)存不再需要時使用free來避免內(nèi)存泄露。
  • 當(dāng)使用realloc時,如果分配失敗,原始內(nèi)存不會被釋放。因此,建議先將realloc的返回值賦給一個臨時指針,以檢查是否分配成功,再重新賦值給原始指針,以避免內(nèi)存泄漏。
  • 始終確保只對通過malloc, calloc, 或 realloc分配的指針使用free,并且每個分配的內(nèi)存塊只被free一次

3.c++內(nèi)存管理方式

C語言內(nèi)存管理方式在C++中可以繼續(xù)使用,但有些地方就無能為力,而且使用起來比較麻煩,因此C++又提出了自己的內(nèi)存管理方式:通過newdelete操作符進(jìn)行動態(tài)內(nèi)存管理

3.1new/delete對內(nèi)置類型的操作

new的基本用法

Type* variable = new Type(arguments);
  • Type:要分配的對象類型
  • variable:指向分配的內(nèi)存的指針
  • arguments:傳遞給構(gòu)造函數(shù)的參數(shù)(如果需要的話)

示例

int* ptr1 = new int; 

在堆上分配了一個int大小的內(nèi)存

int* ptr2 = new int[10]; 

加上方括號[ ]表示分配了十個int大小的內(nèi)存

釋放:

對于ptr,我們直接delete

delete ptr1;

釋放數(shù)組對象的內(nèi)存ptr2,我們需要加上方括號:

delete [] ptr2;

我們也可以分配內(nèi)存的同時直接初始化:

int* ptr5 = new int(5);

動態(tài)申請一個int類型的空間并初始化為5

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
我們也可以同時開辟多個空間完成初始化:

int* ptr6 = new int[10] {1,2,3,4,5};

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
后面的空間默認(rèn)初始化為零

  • 盡管newdelete提供了對象構(gòu)造和析構(gòu)的自動管理,但程序員仍然需要負(fù)責(zé)確保每個用new分配的內(nèi)存都被對應(yīng)的delete釋放,以避免內(nèi)存泄露
  • mallocfree一樣,試圖delete一個未經(jīng)new分配的指針,或者對同一個指針執(zhí)行多次delete,都是未定義行為,并且可能導(dǎo)致程序崩潰
  • 當(dāng)使用new[]分配數(shù)組時,必須使用對應(yīng)的delete[]來釋放內(nèi)存。使用錯誤的delete形式也是未定義行為

來看下面的代碼:

struct ListNode
{
	ListNode* _next;
	int _val;

	ListNode(int val)
		:_next(nullptr)
		,_val(val)
	{}
};

struct ListNode* CreateListNode(int val)
{
	struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	newnode->_next = NULL;
	newnode->_val = val;
	return newnode;
}

這是c語言構(gòu)造一個節(jié)點并完成初始化的過程,我們來看c++的實現(xiàn):

int main()
{
	ListNode* node1 = new ListNode(1);
	return 0;
}

這行代碼自動為ListNode對象分配了內(nèi)存,并調(diào)用了其構(gòu)造函數(shù)進(jìn)行初始化。這種方式更簡潔,也更安全,因為它保證了對象在使用前被正確初始化,注意這里L(fēng)istNode是自定義類型,除了開空間還會調(diào)用構(gòu)造函數(shù)

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

只要我們寫好構(gòu)造函數(shù),我們發(fā)現(xiàn)new的使用是十分方便的

我們來構(gòu)建一個鏈表:

ListNode* CreateList(int n)
{
	ListNode head(-1);  // 哨兵位
	ListNode* tail = &head;
	int val;
	printf("請依次輸入%d個節(jié)點的值:>", n);
	for (size_t i = 0; i < n; i++)
	{
		cin >> val;
		tail->_next = new ListNode(val);
		tail = tail->_next;
	}
	return head._next;
}

我們輸入五個值,1 2 3 4 5
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

哨兵節(jié)點:ListNode head(-1);這行代碼創(chuàng)建了一個局部的哨兵節(jié)點,它的值被設(shè)為-1(這個值通常是任意的,因為哨兵節(jié)點本身不存儲任何有意義的數(shù)據(jù))。哨兵節(jié)點的主要目的是簡化在鏈表頭部的插入和刪除操作,因為你總是有一個非空的節(jié)點作為鏈表的起始點,從而避免了處理空鏈表的特殊情況

最后,函數(shù)通過return head._next;返回新構(gòu)建鏈表的頭節(jié)點。由于head是一個哨兵節(jié)點,它的_next成員實際上指向鏈表的第一個真實節(jié)點(如果有的話),或者是nullptr(如果n為0或用戶沒有輸入任何有效數(shù)據(jù))

3.1.1拋異常

我們不用手動檢查new是否開辟成功new失敗了會拋出異常

void func()
{
	int n = 1;
	while (1)
	{
		int* p = new int[1024 * 1024*100];
		cout <<n<<"->"<< p << endl;
		++n;
	}
}

我們一次申請400M的空間大小
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
再看c語言版本

void func()
{
	int n = 1;
	while (1)
	{
		//int* p = new int[1024 * 1024 * 100];
		int* p = (int*)malloc(1024 * 1024 * 400);
		cout << n << "->" << p << endl;
		++n;
	}
}

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
開辟失敗,程序無限循環(huán)并返回空

c++中的拋異常:

try
	{
		func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

這段代碼是C++中的一個示例,展示了如何使用try-catch語句來處理異常。這里的重點是捕獲并處理func()函數(shù)中可能拋出的異常。如果func()函數(shù)執(zhí)行中出現(xiàn)了問題,它將拋出一個異常,這個異常會被catch塊捕獲。捕獲到的異常類型為const std::exception&,這是C++標(biāo)準(zhǔn)異常類型的一個基類。在catch塊中,通過e.what()調(diào)用來獲取并打印出異常的具體信息

  • try塊:在try塊中的代碼執(zhí)行時,如果發(fā)生了異常(即代碼拋出了異常),那么try塊中的剩余代碼將不會繼續(xù)執(zhí)行,而是跳轉(zhuǎn)到相應(yīng)的catch塊中處理異常

  • catch塊:此代碼段用于捕獲類型為const std::exception&的異常。這意味著它能夠捕獲任何是std::exception實例或其派生類的異常。通過常量引用捕獲異常是一種最佳實踐,因為這樣可以避免異常對象的切片問題,并且可以最小化性能開銷

  • const exception& e:這里聲明了一個名為e的引用,它引用了被捕獲的異常。const限定符表明在catch塊中,e是不會被修改的

  • e.what()std::exception及其派生類有一個名為what()的成員函數(shù),它返回一個描述異常的空終止字符序列(C風(fēng)格字符串)。cout << e.what() << endl;語句將這個消息打印到標(biāo)準(zhǔn)輸出中

后續(xù)我們還會遇到這個函數(shù),再詳細(xì)講解

來看拋異常的結(jié)果:
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

3.2new/delete對自定義類型的操作

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	A* p1 = new A(1);
	delete p1;
	return 0;
}

new/deletemalloc/free最大區(qū)別是 new/delete對于【自定義類型】除了開空間,還會調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)

	A* p1 = new A(1);
00007FF798AA260B  mov         ecx,4  
00007FF798AA2610  call        operator new (07FF798AA104Bh)  
00007FF798AA2615  mov         qword ptr [rbp+108h],rax  
00007FF798AA261C  cmp         qword ptr [rbp+108h],0  
00007FF798AA2624  je          main+50h (07FF798AA2640h)  
00007FF798AA2626  mov         edx,1  
00007FF798AA262B  mov         rcx,qword ptr [rbp+108h]  
00007FF798AA2632  call        A::A (07FF798AA1343h)  
00007FF798AA2637  mov         qword ptr [rbp+138h],rax  
00007FF798AA263E  jmp         main+5Bh (07FF798AA264Bh)  
00007FF798AA2640  mov         qword ptr [rbp+138h],0  
00007FF798AA264B  mov         rax,qword ptr [rbp+138h]  
00007FF798AA2652  mov         qword ptr [rbp+0E8h],rax  
00007FF798AA2659  mov         rax,qword ptr [rbp+0E8h]  
00007FF798AA2660  mov         qword ptr [p1],rax

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
new過程跳轉(zhuǎn)到構(gòu)造函數(shù)
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
delete調(diào)用析構(gòu)函數(shù)

打印結(jié)果如下:

A():000001DB79796B50
~A():000001DB79796B50

我們發(fā)現(xiàn),匯編代碼中有這一步:

00007FF798AA2610  call        operator new (07FF798AA104Bh)

operator new,接下來我們來講解這一部分

4.operator new與operator delete函數(shù)

new和delete是用戶進(jìn)行動態(tài)內(nèi)存申請和釋放的操作符,operator new 和operator
delete是系統(tǒng)提供的全局函數(shù),new在底層調(diào)用operator new全局函數(shù)來申請空間,delete在底層通過operator delete全局函數(shù)來釋放空間

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	void* p;
	while ((p = malloc(size)) == 0)
		if (_callnewh(size) == 0)
		{
			// 如果申請內(nèi)存失敗了,這里會拋出bad_alloc 類型異常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}

operator new該函數(shù)實際通過malloc來申請空間,當(dāng)malloc申請空間成功時直接返回;申請空間失敗,嘗試執(zhí)行空間不足應(yīng)對措施,如果改應(yīng)對措施用戶設(shè)置了,則繼續(xù)申請,否則拋異常。

 static const std::bad_alloc nomem;

申請失敗則會拋異常

void operator delete(void* pUserData)
{
	_CrtMemBlockHeader* pHead;
	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
	if (pUserData == NULL)
		return;
	_mlock(_HEAP_LOCK);  /* block other threads */
	__TRY
		        /* get a pointer to memory block header */
		pHead = pHdr(pUserData);
	         /* verify block type */
	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK);  /* release other threads */
	__END_TRY_FINALLY
		return;
}
_free_dbg(pUserData, pHead->nBlockUse);

operator delete: 該函數(shù)最終是通過free來釋放空間的

通過上述兩個全局函數(shù)的實現(xiàn)知道,operator new 實際也是通過malloc來申請空間,如果malloc申請空間成功就直接返回,否則執(zhí)行用戶提供的空間不足應(yīng)對措施,如果用戶提供該措施就繼續(xù)申請,否則就拋異常。operator delete 最終是通過free來釋放空間的

我們只需要簡單了解一下,并不需要深入理解

5.new和delete的實現(xiàn)原理

如果申請的是內(nèi)置類型的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續(xù)空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL

自定義類型:

  • new的原理
    1. 調(diào)用operator new函數(shù)申請空間
    2. 在申請的空間上執(zhí)行構(gòu)造函數(shù),完成對象的構(gòu)造
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	A* p1 = new A(1);
	delete p1;
	return 0;
}

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

  • delete的原理
    1. 在空間上執(zhí)行析構(gòu)函數(shù),完成對象中資源的清理工作
    2. 調(diào)用operator delete函數(shù)釋放對象的空間
class Stack
{
public:
	Stack()
	{
		_a = (int*)malloc(sizeof(int) * 4);
		_top = 0;
		_capacity = 4;
	}

	~Stack()
	{
		free(_a);
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack* pst = new Stack;
	delete pst;
	return 0;
}

這里進(jìn)行了雙層嵌套:
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
我們就很清楚的能看到,現(xiàn)需要調(diào)用析構(gòu)函數(shù)再進(jìn)行釋放

  • new T[N]的原理

    1. 調(diào)用operator new[]函數(shù),在operator new[]中實際調(diào)用operator new函數(shù)完成N個對象空間的申請
    2. 在申請的空間上執(zhí)行N次構(gòu)造函數(shù)
  • delete[]的原理

    1. 在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個對象中資源的清理
    2. 調(diào)用operator delete[]釋放空間,實際在operator delete[]中調(diào)用operator delete來釋放空間
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	A* p1 = new A;
	A* p2 = new A[10];
	delete p1;
	delete[]p2;
	return 0;
}

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++
在這段代碼中,p2 是指向由 new A[10] 分配的對象數(shù)組的指針。雖然你可能會認(rèn)為 p2 只需要分配足夠存儲 10 個 A 類型對象的空間,即 10 * sizeof(A),實際上編譯器通常會分配額外的空間來存儲有關(guān)數(shù)組本身的信息,比如數(shù)組的大小。這是因為在執(zhí)行 delete[] p2; 時,系統(tǒng)需要知道要調(diào)用多少次析構(gòu)函數(shù)
【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

讓我們具體看一下為什么會這樣:

  1. 對象數(shù)組的內(nèi)存分配:當(dāng)你創(chuàng)建一個對象數(shù)組時,例如 new A[10],C++ 需要知道在稍后釋放數(shù)組時應(yīng)該調(diào)用多少次析構(gòu)函數(shù)。為此,它可能在分配給數(shù)組的內(nèi)存塊中存儲一些額外的元數(shù)據(jù),通常是數(shù)組的長度

  2. 析構(gòu)函數(shù)調(diào)用:在使用 delete[] p2; 釋放內(nèi)存時,這個額外存儲的信息就被用來確保為數(shù)組中的每個元素正確調(diào)用析構(gòu)函數(shù)

  3. 內(nèi)存布局:因此,分配給 p2 的內(nèi)存實際上包含了更多比簡單的 10 * sizeof(A) 字節(jié)。首先是數(shù)組長度的元數(shù)據(jù)(大小取決于系統(tǒng)和編譯器),緊接著是 10 個 A 類型對象的存儲空間

  4. 字節(jié)大小:如果 sizeof(A) 是 4(假設(shè) int 類型是 4 字節(jié),并且沒有類對齊導(dǎo)致的額外空間),那么僅對象部分就占用了 40 字節(jié)。加上存儲數(shù)組大小的額外空間,總大小就會超過 40 字節(jié)

【c/c++】深入探秘:C++內(nèi)存管理的機制,c++筆記倉,c++

我們再來看內(nèi)置類型:

int* p1=new int[10];
00007FF7F031206B  mov         ecx,28h  

剛好開辟了四十個字節(jié)的空間,因為它不需要調(diào)用析構(gòu)函數(shù)

6.簡單了解定位new表達(dá)式(placement-new)

定位new表達(dá)式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個對象

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必須是一個指針,initializer-list是類型的初始化列表

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	int* p1 = new int[10];
	return 0;
}
A* p1 = (A*)malloc(sizeof(A));

p1現(xiàn)在指向的只不過是與A對象相同大小的一段空間,還不能算是一個對象,因為構(gòu)造函數(shù)沒有執(zhí)行

new(p1)A;

顯示調(diào)用構(gòu)造函數(shù)對一塊已經(jīng)有的空間的初始化

定位new表達(dá)式在實際中一般是配合內(nèi)存池使用。因為內(nèi)存池分配出的內(nèi)存沒有初始化,所以如果是自定義類型的對象,需要使用new的定義表達(dá)式進(jìn)行顯示調(diào)構(gòu)造函數(shù)進(jìn)行初始化

7.概念辨析

7.1 malloc/free和new/delete的區(qū)別

malloc/free和new/delete的共同點是:都是從堆上申請空間,并且需要用戶手動釋放。不同的地方是:

  1. malloc和free是函數(shù),new和delete是操作符
  2. malloc申請的空間不會初始化,new可以初始化
  3. malloc申請空間時,需要手動計算空間大小并傳遞,new只需在其后跟上空間的類型即可,如果是多個對象,[]中指定對象個數(shù)即可
  4. malloc的返回值為void*, 在使用時必須強轉(zhuǎn),new不需要,因為new后跟的是空間的類型
  5. malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
  6. 申請自定義類型對象時,malloc/free只會開辟空間,不會調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而new在申請空間后會調(diào)用構(gòu)造函數(shù)完成對象的初始化,delete在釋放空間前會調(diào)用析構(gòu)函數(shù)完成空間中資源的清理

7.2 內(nèi)存泄漏

  • 什么是內(nèi)存泄漏:內(nèi)存泄漏指因為疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因為設(shè)計錯誤,失去了對該段內(nèi)存的控制,因而造成了內(nèi)存的浪費。
  • 內(nèi)存泄漏的危害:長期運行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺服務(wù)等等,出現(xiàn)內(nèi)存泄漏會導(dǎo)致響應(yīng)越來越慢,最終卡死

分類:

  • 堆內(nèi)存泄漏(Heap leak):
    堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過malloc / calloc / realloc / new等從堆中分配的一塊內(nèi)存,用完后必須通過調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計錯誤導(dǎo)致這部分內(nèi)存沒有被釋放,那么以后這部分空間將無法再被使用,就會產(chǎn)生Heap Leak

  • 系統(tǒng)資源泄漏
    指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒有使用對應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費,嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定

本節(jié)內(nèi)容到此結(jié)束!!求大家三連啊!文章來源地址http://www.zghlxwxcb.cn/news/detail-853760.html

到了這里,關(guān)于【c/c++】深入探秘:C++內(nèi)存管理的機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 深入探討Java虛擬機(JVM):執(zhí)行流程、內(nèi)存管理和垃圾回收機制

    深入探討Java虛擬機(JVM):執(zhí)行流程、內(nèi)存管理和垃圾回收機制

    目錄 什么是JVM? JVM 執(zhí)行流程 JVM 運行時數(shù)據(jù)區(qū) 堆(線程共享) Java虛擬機棧(線程私有) 什么是線程私有? 程序計數(shù)器(線程私有) 方法區(qū)(線程共享) JDK 1.8 元空間的變化 運行時常量池 內(nèi)存布局中的異常問題 1.? Java堆溢出 2.??虛擬機棧和本地方法棧溢出 JVM 類加載 1.

    2024年02月09日
    瀏覽(27)
  • 深入理解C++內(nèi)存管理

    深入理解C++內(nèi)存管理

    C++的高抽象層次,又兼具高性能,是其他語言所無法替代的,C++標(biāo)準(zhǔn)保持穩(wěn)定發(fā)展,更加現(xiàn)代化,更加強大。但在各種活躍編程語言中,C++門檻依然很高,尤其C++的內(nèi)存問題(內(nèi)存泄露,內(nèi)存溢出,內(nèi)存宕機,堆棧破壞等問題),需要理解C++標(biāo)準(zhǔn)對象模型,C++標(biāo)準(zhǔn)庫,標(biāo)準(zhǔn)

    2023年04月08日
    瀏覽(28)
  • Python內(nèi)存管理與垃圾回收機制:深入理解與優(yōu)化【第138篇—RESTful API】

    Python內(nèi)存管理與垃圾回收機制:深入理解與優(yōu)化【第138篇—RESTful API】

    前些天發(fā)現(xiàn)了一個巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。【點擊進(jìn)入巨牛的人工智能學(xué)習(xí)網(wǎng)站】。 在Python編程中,內(nèi)存管理與垃圾回收機制是至關(guān)重要的主題。了解Python如何管理內(nèi)存和處理垃圾回收對于編寫高效、穩(wěn)定的程序至關(guān)重要。本

    2024年03月18日
    瀏覽(21)
  • Go指針探秘:深入理解內(nèi)存與安全性

    Go指針探秘:深入理解內(nèi)存與安全性

    Go指針為程序員提供了對內(nèi)存的深入管理能力,同時確保了代碼的安全性。本文深入探討了Go指針的基礎(chǔ)概念、操作、深層理解及其特性與限制。通過深入了解其設(shè)計哲學(xué)和應(yīng)用,我們可以更好地利用Go的強大功能。 關(guān)注公眾號【TechLeadCloud】,分享互聯(lián)網(wǎng)架構(gòu)、云服務(wù)技術(shù)的

    2024年02月08日
    瀏覽(24)
  • [筆記]深入解析Windows操作系統(tǒng)《四》管理機制

    [筆記]深入解析Windows操作系統(tǒng)《四》管理機制

    本章講述了Microsoft Windows操作系統(tǒng)中四種基本的機制,它們對于系統(tǒng)的管理和配置至關(guān)重要,它們是: 注冊表(Registry) 服務(wù)(Service) 統(tǒng)一的背景進(jìn)程管理器(UBPM) Windows管理設(shè)施(WMI) Windows診斷基礎(chǔ)設(shè)施(WDI) 注冊表在Windows系統(tǒng)的配置和控制方面扮演了一個非常關(guān)鍵的角色。 它既是系

    2024年02月04日
    瀏覽(24)
  • “深入解析JVM內(nèi)部機制:探秘Java虛擬機的奧秘“

    標(biāo)題:深入解析JVM內(nèi)部機制:探秘Java虛擬機的奧秘 摘要:本文將深入解析JVM(Java虛擬機)的內(nèi)部機制,從字節(jié)碼執(zhí)行到垃圾回收,逐步揭示Java程序運行的奧秘。通過理論分析和示例代碼,讀者將對JVM的工作原理有更深入的了解。 正文: 一、Java虛擬機簡介 Java虛擬機(JVM)

    2024年02月12日
    瀏覽(25)
  • 深入源碼解析ArrayList:探秘Java動態(tài)數(shù)組的機制與性能

    深入源碼解析ArrayList:探秘Java動態(tài)數(shù)組的機制與性能

    1.1 介紹ArrayList的基本概念和作用 在Java中,ArrayList是一個實現(xiàn)了List接口的動態(tài)數(shù)組。它可以根據(jù)需要自動增加大小,因此可以存儲任意數(shù)量的元素。 基本概念: ArrayList是Java中常用的集合類之一,它可以存儲對象,并且可以根據(jù)索引訪問和操作這些對象。 ArrayList是基于數(shù)組

    2024年02月04日
    瀏覽(33)
  • 深入理解C++中的堆與棧:內(nèi)存管理的關(guān)鍵區(qū)別與實例解析

    深入理解C++中的堆與棧:內(nèi)存管理的關(guān)鍵區(qū)別與實例解析

    ? 概述: C++中,堆和棧是兩種不同的內(nèi)存分配方式。棧自動分配、釋放內(nèi)存,適用于短生命周期變量;堆需要手動管理,適用于動態(tài)分配內(nèi)存,但需要顯式釋放以防內(nèi)存泄漏。通過清晰的示例源代碼,演示了它們在變量生命周期、訪問方式等方面的區(qū)別。 C++中的堆(heap)和

    2024年02月22日
    瀏覽(31)
  • 【C++】深入探討內(nèi)存管理:malloc/free與new/delete的區(qū)別以及如何避免內(nèi)存泄漏

    在軟件開發(fā)中,正確處理內(nèi)存管理是至關(guān)重要的一環(huán)。在C++編程中,我們經(jīng)常會用到動態(tài)內(nèi)存管理的工具,比如 malloc/free 和 new/delete 。本文將深入探討 malloc/free 與 new/delete 之間的區(qū)別,以及如何有效地避免內(nèi)存泄漏問題。 都是用于從堆上申請空間,并需要手動釋放。 mallo

    2024年02月22日
    瀏覽(21)
  • 【C++深入淺出】C/C++內(nèi)存管理(教你如何new到對象)

    【C++深入淺出】C/C++內(nèi)存管理(教你如何new到對象)

    ? ? ? ? 前面我們學(xué)習(xí)了有關(guān)C++類和對象的知識,學(xué)會了如何構(gòu)建一個完整的類,這些類都是存儲在 棧空間 上的。在C語言中,我們不僅可以在棧上定義變量,也可以對 堆 上的空間進(jìn)行管理,在接下來的幾期中,我們的目標(biāo)就是 學(xué)會C++中是如何進(jìn)行內(nèi)存管理的 。 ? ? ? ?

    2024年02月08日
    瀏覽(31)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包