目錄
1. C/C++內存分布
練習:
2. C語言動態內存管理方式
2.1?malloc/calloc/realloc的區別
2.2 malloc的實現原理
2.3 內存塊分布與擴容
3. C++動態內存管理方式
3.1 new/delete操作類內置類型
1. new操作內置類型
2. delete操作內置類型
3.2?new/delete操作類自定義類型
1. new操作自定義類型
2.?delete操作自定義類型
4.?operator new與operator delete函數
4.1 operator new??函數
4.2?operator delete函數
4.3 與new/delete操作符的關系
5. new和delete的實現原理
5.1 內置類型
5.2 自定義類型
6.?new/delete?和 malloc/free的區別
1. 所屬語言
2. 操作對象與功能
3. 數組操作
4. 內存不足處理
5. 重載與自定義
7. 總結:
1. C/C++內存分布
在C和C++中 , 程序的內存分布通常可劃分為幾個主要區域 , 這些區域各自承擔不同的功能 , 具體如下:
1. 棧(Stack)
- 特點 : 由編譯器自動管理 , 空間較小(通常幾MB) , 遵循“先進后出”原則。
- 存儲內容 : 函數的局部變量 , 函數參數 , 返回地址等。
- 示例 : 函數內部定義的?int a = 10;,a就存儲在棧中 , 函數執行結束后會自動釋放。
2. 堆(Heap)
- 特點 : 由程序員手動管理(C中用?malloc?/?free , C++中用?new?/?delete) , 空間較大(可達GB級別) , 分配和釋放需要顯式操作。
- 存儲內容 : 動態分配的內存 , 比如動態創建的對象、數組等。
- 示例 : int* p = new int[10]; , 數組的內存就位于堆中 , 需用?delete[] p?手動釋放 , 否則可能導致內存泄漏。
3. 全局/靜態存儲區(Global/Static Storage Area)(數據段)
- 特點 : 程序運行期間一直存在 , 由編譯器管理 , 程序結束后自動釋放。
- 存儲內容:
- 全局變量(定義在函數外的變量);
- 靜態變量(用?static?修飾的變量 , 包括全局靜態變量和局部靜態變量)。
- 示例 : int g_var = 20;(全局變量) , static int s_var = 30;(靜態變量) , 都存儲在此區域。
4. 常量存儲區(Constant Storage Area)(代碼段)
- 特點 : 存放常量 , 內容不可修改 , 程序結束后釋放。
- 存儲內容 : 字符串常量(如"hello") , const?修飾的常量(全局或靜態的?const?變量 , 局部const?變量可能在棧中)。
- 示例 : const int c_var = 40;(全局常量) , char* str = "world";("world"?存于常量區)。
5. 代碼區(Code Segment/Text Segment)
- 特點 : 存放程序的機器指令(二進制代碼) , 通常為只讀 , 以防止意外修改。
- 作用 : CPU從這里讀取指令并執行程序。
總結來說 , 棧和堆用于動態管理運行時數據 , 全局/靜態區和常量區存儲生命周期較長的數據 , 代碼區則負責存儲程序的執行指令 , 這些區域共同構成了C和C++程序的內存布局。
練習:
?以下是對每個變量內存位置的詳細分析:
- globalVar :?屬于全局變量 , 存儲在數據段(靜態區 , 選項C)。全局變量在程序整個運行周期都存在 , 由編譯器管理其內存 , 程序結束時自動釋放。
- staticGlobalVar : 是全局靜態變量,同樣存儲在數據段(靜態區 ,?選項C)。靜態全局變量作用域限制在定義的文件內 , 但存儲區域和全局變量一樣 , 在數據段 , 生命周期貫穿程序運行。
- staticVar : 為函數內的靜態變量 , 存儲在數據段(靜態區 ,?選項C)。函數內的靜態變量 , 在第一次函數調用時初始化 , 之后一直存在于數據段 , 直到程序結束。
- localVar :?是函數內的局部變量 , 存儲在棧(選項A)。局部變量在函數調用時在棧上分配空間 , 函數執行完畢 , 棧空間自動釋放。
- num1 :?是函數內的局部數組 , 存儲在棧(選項A)。局部數組屬于局部變量 , 在棧上分配內存 , 函數結束后棧空間回收。
- char2 :?char2?是函數內的字符數組 , 存儲在棧(選項A);而?"abcd"?作為字符串常量 , 存儲在代碼段(常量區 ,?選項D) , 數組?char2?是在棧上 , 將常量區的字符串內容拷貝過來。
- *char2 :?char2?是棧上的數組 , *char2?是數組的第一個字符 , 所以存儲在棧(選項A)。
- pChar3 :?pChar3?是指針變量 , 存儲在棧(選項A) , 它指向的是字符串常量?"abcd"。
- *pChar3 :?pChar3?指向的字符串常量?"abcd"?存儲在代碼段(常量區 ,?選項D) , 所以*pChar3(即字符串的字符)在代碼段(常量區?, 選項D)。
- ptr1 : ptr1?是指針變量 , 存儲在棧(選項A) , 它指向堆上的內存。
- *ptr1 : ptr1?通過?malloc?動態分配的內存位于堆(選項B) , 所以*ptr1(即所指向的內存區域)在堆(選項B)。
2. C語言動態內存管理方式
2.1?malloc/calloc/realloc的區別
在前面的C語言中 , 我們學習了C語言的動態動態內存管理方式 , 其中主要學了在malloc , calloc , realloc?和free等函數 , 下面我們再簡單回顧一下它們之間的區別:
在C語言中 , malloc , calloc , realloc?和free是實現動態內存管理的重要函數 , 它們各自有不同的功能和使用方式:
1. malloc函數
- 函數原型:void* malloc(size_t size);?
- 功能:在堆內存中分配指定字節數的連續內存空間。函數返回一個指向分配內存起始地址的指針 , 如果分配失敗(例如內存不足) , 則返回NULL?。
- 注意事項:
- malloc?分配的內存空間中的值是未初始化的 , 可能是任意值。
- 使用完 malloc?分配的內存后 , 必須調用 free?函數釋放 , 否則會導致內存泄漏。
- 由于?malloc?返回的是 void*?類型指針 , 在賦值給其他類型指針時 , 需要進行強制類型轉換。
2. calloc函數
- - 函數原型:void* calloc(size_t num, size_t size);?
- - 功能 : 在堆內存中分配 num?個大小為 size?字節的連續內存空間 , 并將這些空間初始化為0。它返回一個指向分配內存起始地址的指針 , 如果分配失敗 , 則返回?NULL?。
- 注意事項:
- 相比 malloc , calloc會自動進行初始化操作 , 適合需要初始值為0的場景?, 不過也因為多了初始化操作 , 在性能上會有一定開銷。
- 同樣 , 使用完后要調用 free?釋放內存。
3. realloc函數
- 函數原型:? void* realloc(void* ptr, size_t size);?
- 功能:用于重新分配已分配的內存空間。ptr?是指向之前由 malloc , calloc?或 realloc?分配的內存塊的指針 , size?是新的內存塊大小。如果?ptr?為 NULL?, 則?realloc?相當于malloc;如果?size?為0且?ptr?不為NULL , 則釋放 ptr?指向的內存塊 , 相當于 ?free(ptr)?。
- 注意事項:
- realloc?可能會移動原來內存塊的位置 , 因為它要根據當前內存的使用情況和新的大小來決定是否重新分配一塊新的連續內存空間。所以在調用 realloc?后 , 要使用其返回的新指針 , 而不能再使用原來的指針。
- 如果重新分配失敗 , 原來 ptr?指向的內存塊不會被釋放 , 仍然有效。
4. free函數
- 函數原型:void free(void* ptr);?
- 功能:釋放由 malloc , calloc?或 realloc?分配的內存空間 , 讓系統可以回收并重新利用這部分內存。ptr?是指向要釋放的內存塊的指針。
- 使用示例:在前面介紹?malloc , calloc , realloc?的示例中都有使用free釋放內存的操作。
- 注意事項:
- 只能釋放由 malloc , calloc , realloc?分配的內存 , 釋放其他內存會導致未定義行為。
- 不能多次釋放同一塊內存 , 也不能釋放 NULL?指針(雖然釋放?NULL?指針不會報錯 , 但也無實際意義)。
#include <stdio.h>
#include <stdlib.h> // 包含動態內存管理函數的頭文件int main() {int *ptr1, *ptr2, *ptr3;// 1. 使用 malloc 分配內存(未初始化)ptr1 = (int*)malloc(3 * sizeof(int)); // 分配3個int的空間// 2. 使用 calloc 分配內存(自動初始化為0)ptr2 = (int*)calloc(4, sizeof(int)); // 分配4個int的空間// 3. 使用 realloc 調整已分配的內存(基于ptr1擴展)ptr3 = (int*)realloc(ptr1, 5 * sizeof(int)); // 將ptr1的3個int擴展為5個// 4. 釋放所有動態分配的內存free(ptr3); // 釋放realloc返回的新地址(原ptr1已被覆蓋,無需重復釋放)free(ptr2); // 釋放calloc分配的內存return 0;
}
2.2 malloc的實現原理
malloc?是C語言中用于動態分配內存的函數 , 其工作原理可簡要概括為:
- 內存池管理:程序運行時會有一塊預設的堆內存區域(內存池) , malloc?負責管理這塊區域 , 而非直接向操作系統申請內存。
- 分配過程:當調用 malloc?申請指定大小的內存時 , 它會在內存池中查找足夠大的空閑內存塊 , 劃分出所需大小的部分 , 返回指向該部分的指針 , 并記錄該內存塊的使用信息(如大小、狀態)。
- 內存池擴容:若內存池中沒有足夠的空閑內存 , malloc?會通過系統調用(如 Linux 中的 ?brk?或 mmap)向操作系統申請更多內存 , 擴充內存池后再進行分配。
- 配合釋放操作:當通過 free?釋放內存時 , malloc?會根據之前記錄的信息回收內存塊 , 標記為空閑 , 并可能合并相鄰的空閑塊 , 以減少內存碎片 , 提高后續分配效率。
2.3 內存塊分布與擴容
動態內存分配中:
- 內存塊分布:已分配的內存塊和空閑塊在堆區混雜存在。當申請新內存時 , 若有合適大小的空閑塊 , 直接分配;
- 擴容:若要給已分配的內存塊擴容(如用?realloc ) ,? 如果原內存塊后有足夠空閑空間 , 直接原地擴大;若沒有 , 就新找一塊足夠大的連續空間 , 復制原數據后釋放舊空間 , 返回新地址。
3. C++動態內存管理方式
C語言的動態內存管理方式在C++中仍然可以繼續使用 , 但是有些地方就會顯得無能為力了 , 而且使用起來比較麻煩 , 因此C++又提出了一種屬于自己的動態內存管理方式 , 通過new和delete操作符進行動態內存管理。
3.1 new/delete操作類內置類型
在 C++ 中 , new?和?delete?操作內置類型(如 int , double , char??等)時 , 流程相對簡單 , 不涉及對象的構造和析構(因為內置類型沒有構造函數和析構函數) , 核心是內存的分配與釋放:
1. new操作內置類型
分配單個元素:如?int* p = new int;?
- 步驟 : 向堆區申請一塊能容納?int?類型的內存空間 , 返回指向該空間的指針。
- 注意 : 默認不初始化 , 內存中的值是隨機的;若要初始化 , 可寫成?int* p = new int(10); , 此時內存會被初始化為10。
分配數組:如 int* arr = new int[5];?
- 步驟 : 向堆區申請能容納5個?int?的連續內存空間 , 返回指向首元素的指針。
- 注意 : 默認不初始化;若要初始化所有元素為 0 , 可寫成?int* arr = new int[5]();?。
2. delete操作內置類型
釋放單個元素:如?delete p;?
- 步驟:直接釋放?p?指向的堆內存(無需額外清理 , 因無析構函數) , p?變為野指針(建議置為 nullptr)。
釋放數組:如 ?delete[] arr;?
- 步驟:釋放?arr?指向的整個連續數組內存 , 必須用 delete[]??匹配 new[] (雖然內置類型下用 delete?可能不報錯 , 但會導致行為未定義 , 是錯誤寫法)。
簡言之 , 內置類型的?new?/?delete?核心是“分配/釋放堆內存” , 比自定義類型少了構造/析構步驟 , 但仍需嚴格匹配?new?與 delete , new[]?與?delete[]。
3.2?new/delete操作類自定義類型
new?和 delete?操作自定義類型時 , 會完整包含“內存管理”和“對象生命周期管理”兩個環節 , 具體流程如下:
1. new操作自定義類型
以類?A?為例分配單個對象:如 A* p = new A;?
- 1.?先調用底層內存分配函數(類似?malloc) , 在堆區申請一塊能容納 A?類型對象的內存。
- 2.?自動調用 A?的構造函數(根據參數匹配對應構造函數 , 如無參數則調用默認構造函數) , 初始化這塊內存中的對象(例如初始化成員變量 , 申請資源等)。
- 3.?返回指向該對象的指針?p。
分配數組:如?A* arr = new A[3];?
- 1.?申請能容納3個?A?對象的連續堆內存。
- 2.?依次調用3次?A?的構造函數(每個元素對應一次) , 初始化數組中的每個對象。
2.?delete操作自定義類型
釋放單個對象:如 delete p;?
- 1.?先調用 p?指向對象的析構函數(?A::~A() ) , 清理對象內部資源(例如釋放成員變量指向的堆內存 , 關閉文件等)。
- 2.?調用底層內存釋放函數(類似?free ) , 將對象占用的堆內存歸還給系統。
- 3.?指針?p?變為野指針 , 建議置為 nullptr。
釋放數組:如 delete[] arr;?
- 1.?先按相反順序依次調用數組中每個A對象的析構函數(共 3 次) ,?清理所有元素的資源。
- 2.?釋放整塊連續內存。
- 注意 : 必須用 delete[]?匹配 new[] , 否則會導致部分對象析構函數不被調用 , 造成資源泄漏。
核心區別:自定義類型的 new?/?delete?比內置類型多了構造函數初始化對象和析構函數清理資源的步驟 , 這是C++面向對象特性在內存管理中的直接體現 , 確保對象從創建到銷毀的完整生命周期管理。
//new和delete操作自定義類型
#include<iostream>
using namespace std;class A
{
public:A(int a = 0) //構造函數: _a(a){cout << "A():" << this << endl;}~A() //析構函數{cout << "~A():" << this << endl;}
private:int _a;
};int main()
{// new/delete 和 malloc/free最大區別是 new/delete對于【自定義類型】除了開空間還會調用構造函數和析構函數//自定義類型A* p1 = (A*)malloc(sizeof(A));free(p1);A* p2 = new A(1);delete p2;//內置類型是幾乎是一樣的int* p3 = (int*)malloc(sizeof(int)); free(p3);int* p4 = new int;delete p4;A* p5 = (A*)malloc(sizeof(A) * 10);free(p5);A* p6 = new A[10];delete[] p6;return 0;
}
輸出結果:
4.?operator new與operator delete函數
new和delete是用戶進行動態內存申請和釋放的操作符 , operator new 和operator delete是系統提供的全局函數 , new在底層調用operator new全局函數來申請空間 , delete在底層通過operator delete全局函數來釋放空間。可以理解為operator new?和 operator delete?是 new?和?delete?操作符的底層支撐 , 主要負責內存的分配與釋放(不涉及對象的構造和析構):
4.1 operator new??函數
- 功能:負責在堆上分配指定大小的內存塊 , 與 C 語言中的?malloc?類似 , 但行為有差異(operator new?分配失敗時默認拋出 std::bad_alloc?異常 , 而?malloc 則會返回 NULL)。
- 函數原型(簡化版) : void* operator new(size_t size); 其中 size?是要分配的內存字節數。
- 使用示例:
#include <iostream> #include <new> ?// 包含相關異常定義等int main() {// 分配能容納一個 int 類型的內存int* p = (int*)operator new(sizeof(int));if (p == nullptr) {std::cout << "內存分配失敗" << std::endl;return 1;}*p = 10;std::cout << *p << std::endl;operator delete(p);return 0; }
- 特點:
- 只分配內存 , 不調用對象的構造函數(這是和 new?操作符的關鍵區別 , new?會在 operator new?分配內存后調用構造函數)。
- 可以被重載 , 用戶能自定義內存分配的方式(比如使用內存池等)。
4.2?operator delete函數
- 功能 : 負責釋放由?operator new?分配的內存塊 , 與 C 語言中的?free?類似。
- 函數原型(簡化版) : void operator delete(void* ptr); 其中 ptr?是要釋放的內存塊指針。
- 使用示例 : 如上面的示例 , 在使用 operator new?分配內存后 , 通過 operator delete(p)?釋放內存。
- 特點:
- 只釋放內存 , 不調用對象的析構函數(這是和 delete?操作符的關鍵區別 , delete?會在調用 operator delete?釋放內存前調用析構函數)。
- 同樣可以被重載 , 用戶能自定義內存釋放的邏輯。
4.3 與new/delete操作符的關系
- new?操作符的執行過程 : 先調用 operator new?分配內存 , 然后調用對象的構造函數初始化對象。
- delete?操作符的執行過程 : 先調用對象的析構函數清理對象 , 然后調用?operator delete?釋放內存。
- 簡單來說 , operator new?和 operator delete?是更底層的內存分配/釋放工具 , new?和?delete?則是在它們的基礎上 , 結合了對象的構造和析構操作 , 更符合 C++ 面向對象的特性。
5. new和delete的實現原理
5.1 內置類型
- 如果申請的是內置類型的空間 , new和malloc , delete和free基本類似 , 不同的地方是:new/delete申請和釋放的是單個元素的空間 , new[]和delete[]申請的是連續空間 , 而且new在申請空間失敗時會拋異常 , malloc會返回NULL。
5.2 自定義類型
new的原理
- 1. 調用operator new函數申請空間
- 2. 在申請的空間上執行構造函數 , 完成對象的構造
delete的原理
- 1. 在空間上執行析構函數 , 完成對象中資源的清理工作
- 2. 調用operator delete函數釋放對象的空間
new T[N]的原理
- 1. 調用operator new[]函數 , 在operator new[]中實際調用operator new函數完成N個對象空間的申請
- 2. 在申請的空間上執行N次構造函數
delete[]的原理
- 1. 在釋放的對象空間上執行N次析構函數 , 完成N個對象中資源的清理
- 2. 調用operator delete[]釋放空間 , 實際在operator delete[]中調用operator delete來釋放空間
6.?new/delete?和 malloc/free的區別
new/delete?和 malloc/free 都是 C++(及C)中用于動態內存管理的工具 , 主要區別如下:
1. 所屬語言
- malloc?/?free?是 C 語言的標準庫函數 , 在 C++ 中也可使用 , 但更偏向于底層內存操作。
- new?/?delete?是 C++ 的操作符 , 是 C++ 面向對象特性在內存管理上的體現。
2. 操作對象與功能
- malloc?/?free?:
- 僅負責內存的分配與釋放 , 不涉及對象的構造和析構(因為C是面向過程語言 , 無“對象”概念)。
- malloc?需手動計算內存大小(如?malloc(sizeof(int)) ) , 返回?void* , 使用時通常需要強制類型轉換。
- free?只需傳入要釋放的內存指針。
- new?/?delete?:
- 不僅分配/釋放內存 , 還會自動調用對象的構造函數(?new?時)和析構函數(?delete?時)(針對自定義類型)。
- new?無需手動計算類型大小(如?new int?會自動分配 int?大小的內存) , 直接返回對應類型的指針 , 無需強制類型轉換。
- delete?只需傳入對象指針。
3. 數組操作
- malloc?/?free?:
- 分配數組需手動計算總內存大小(如 ?malloc(5 * sizeof(int)) ) , 釋放時直接 free?指針 , 不涉及數組元素的“逐個清理”(因為無析構函數)。
- new?/?delete?:
- 分配數組用 ?new[] (如?new int[5] ) , 會為數組中每個元素(若為自定義類型)調用構造函數;釋放數組用?delete[] , 會為每個元素調用析構函數 , 然后釋放整塊內存。若用 delete?釋放?new[]?分配的數組(自定義類型) , 會因析構函數調用不完整導致內存泄漏或錯誤。
4. 內存不足處理
- malloc : 內存不足時返回 NULL , 需手動檢查返回值。
- new : 內存不足時默認拋出 std::bad_alloc?異常 , 可通過異常處理機制捕獲 , 也可自定義 new?的行為(如設置內存不足的回調)。
5. 重載與自定義
- malloc?/?free : 不可重載 , 行為固定。
- new?/?delete : 可以重載 , 用戶可自定義內存分配和釋放的邏輯(比如使用內存池等) , 更靈活地滿足特定需求。
簡單來說 , new?/?delete?是更“面向對象”的內存管理方式 , 封裝了對象構造/析構的邏輯;而 malloc?/?free?更底層 , 僅關注內存塊的分配與釋放。在 C++ 中 , 更推薦使用 new?/?delete (尤其是處理自定義類型時) , 以利用其對對象生命周期的管理能力。
7. 總結:
本文系統介紹了C/C++程序的內存分布與動態內存管理機制。內存主要分為棧、堆、全局/靜態區、常量區和代碼區五部分 , 各自承擔不同功能。C語言通過malloc/calloc/realloc/free進行動態內存管理 , 而C++引入了更高級的new/delete操作符 , 不僅能分配內存還能自動調用構造/析構函數。文章詳細對比了malloc/free與new/delete的區別 , 并深入解析了operator new/delete的實現原理。關鍵在于:C++的new/delete是面向對象的內存管理方式 , 而malloc/free只進行原始內存操作。在C++開發中 , 特別是處理自定義類型時 , 應優先使用new/delete以確保完整的對象生命周期管理。
最后 , 感謝大家的觀看!