存管理)
內(nèi)存分布高地址 ↑ ├─────────────┤ │ 棧 │ ← 向下增長局部變量、函數(shù)參數(shù) ├─────────────┤ │ │ │ │ ↓ │ ├─────────────┤ │ 內(nèi)存映射段 │ ← 共享庫、共享內(nèi)存 ├─────────────┤ │ ↑ │ │ │ │ ├─────────────┤ │ 堆 │ ← 向上增長動(dòng)態(tài)分配 ├─────────────┤ │ 數(shù)據(jù)段 │ ← 全局變量、靜態(tài)變量 ├─────────────┤ │ 代碼段 │ ← 可執(zhí)行代碼、常量 └─────────────┘ 低地址 includeiostream using namespace std; int globalVar 1; // 全局變量 static int staticGlobalVar 1; // 靜態(tài)全局變量 void Test() { static int staticVar 1; // 靜態(tài)局部變量 int localVar 1; // 局部變量 int num1[10] {1, 2, 3, 4}; // 局部數(shù)組 char char2[] abcd; // 局部數(shù)組存儲(chǔ)字符串的副本 const char* pChar3 abcd; // 指針變量指向常量字符串 int* ptr1 (int*)malloc(sizeof(int) * 4); // 動(dòng)態(tài)分配 int* ptr2 (int*)calloc(4, sizeof(int)); // 動(dòng)態(tài)分配 int* ptr3 (int*)realloc(ptr2, sizeof(int) * 4); // 重新分配 free(ptr1); free(ptr3); // 注意ptr2被realloc處理了不需要再free } int main() { Test(); return 0; }說明棧又叫做堆?!鎯?chǔ)非靜態(tài)局部變量/函數(shù)參數(shù)/返回值等向下增長內(nèi)存映射段是高效的I/O映射方式用于裝載一個(gè)共享的動(dòng)態(tài)內(nèi)存庫。堆是用于內(nèi)存的動(dòng)態(tài)內(nèi)存分配向上增長系統(tǒng)一般給的內(nèi)存都比較大數(shù)據(jù)段用于存儲(chǔ)全局變量和靜態(tài)變量代碼段用于可執(zhí)行的代碼/只讀常量動(dòng)態(tài)內(nèi)存管理malooc/calloc/realloc的區(qū)別#includeiostream using namespace std; void Test() { // 1. malloc - 分配未初始化的內(nèi)存 int* p1 (int*)malloc(sizeof(int) * 4); if (p1 NULL) { perror(malloc fail); return; } // 2. calloc - 分配并初始化為0 int* p2 (int*)calloc(4, sizeof(int)); // calloc(4, sizeof(int)) 等價(jià)于 malloc(4 * sizeof(int)) 內(nèi)存置0 // 3. realloc - 調(diào)整已分配內(nèi)存的大小 int* p3 (int*)realloc(p2, sizeof(int) * 10); // 注意如果realloc成功p2會(huì)被自動(dòng)處理不需要free(p2) if (p3 NULL) { free(p2); // realloc失敗時(shí)原內(nèi)存不變需要手動(dòng)釋放 return; } // 釋放內(nèi)存 free(p1); free(p3); // p2已經(jīng)被realloc處理不需要單獨(dú)free }內(nèi)存管理方式C語言的內(nèi)存管理方式在C也可以使用但是也有一些欠缺點(diǎn)用起來比較麻煩。因此C有自己新的內(nèi)存管理方式——通過操作符new和delete用法講解#includeiostream using namespace std; int main() { // 1. 基本用法申請(qǐng)和釋放單個(gè)對(duì)象 int* p1 new int; // 申請(qǐng)一個(gè) int未初始化 int* p2 new int[10]; // 申請(qǐng) 10 個(gè) int 的數(shù)組未初始化 delete p1; // 釋放單個(gè)對(duì)象 delete[] p2; // 釋放數(shù)組 // 2. 初始化用法申請(qǐng)時(shí)初始化 int* p3 new int(0); // 申請(qǐng)一個(gè) int初始化為 0 int* p4 new int[10]{ 0 }; // 申請(qǐng) 10 個(gè) int全部初始化為 0 int* p5 new int[10]{ 1, 2, 3, 4, 5 }; // 前5個(gè)初始化后面默認(rèn)為0 delete p3; // 釋放單個(gè)對(duì)象 delete[] p4; // 釋放數(shù)組 delete[] p5; // 釋放數(shù)組 return 0; }注意new 和 delete 必須配對(duì)使用new - deletenew[] - delete []應(yīng)用示例struct ListNode { int val; ListNode* next; ListNode(int x) : val(x) , next(nullptr) {} }; int main() { // 1. 自定義類型 A 的 new/delete假設(shè) A 存在 // A* p1 new A; // 調(diào)用默認(rèn)構(gòu)造 // A* p2 new A(1); // 調(diào)用帶參構(gòu)造 // delete p1; // delete p2; // 2. 創(chuàng)建鏈表節(jié)點(diǎn) ListNode* n1 new ListNode(1); ListNode* n2 new ListNode(2); ListNode* n3 new ListNode(3); ListNode* n4 new ListNode(4); // 3. 連接節(jié)點(diǎn)形成鏈表 n1-next n2; n2-next n3; n3-next n4; n4-next nullptr; // 尾節(jié)點(diǎn)指向 nullptr已經(jīng)由構(gòu)造函數(shù)設(shè)置 // 4. 遍歷鏈表 ListNode* cur n1; while (cur ! nullptr) { cout cur-val - ; cur cur-next; } cout nullptr endl; // 5. 釋放鏈表內(nèi)存重要 cur n1; while (cur ! nullptr) { ListNode* next cur-next; // 保存下一個(gè)節(jié)點(diǎn) delete cur; // 釋放當(dāng)前節(jié)點(diǎn) cur next; // 移動(dòng)到下一個(gè) } return 0; }可以看到寫鏈表的時(shí)候可以直接傳參初始化不需要寫Init。從應(yīng)用層面我們可以看到創(chuàng)造new和delete的原因是為了給自定義類型使用的因?yàn)樽远x類型往往都需要調(diào)用默認(rèn)構(gòu)造函數(shù)更為方便對(duì)應(yīng)內(nèi)置類型和malloc和free的區(qū)別不大自定義類型int main() { // 1. 單個(gè)對(duì)象使用 new 申請(qǐng)并構(gòu)造 A* p1 new A(1); // 調(diào)用 A(int) 構(gòu)造函數(shù) A* p2 new A(2, 2); // 調(diào)用 A(int, int) 構(gòu)造函數(shù) // 2. 對(duì)象數(shù)組使用已有對(duì)象拷貝構(gòu)造 A aa1(1, 1); A aa2(2, 2); A aa3(3, 3); A* p3 new A[3]{ aa1, aa2, aa3 }; // 調(diào)用拷貝構(gòu)造 // 3. 對(duì)象數(shù)組使用臨時(shí)對(duì)象 A* p4 new A[3]{ A(1, 1), A(2, 2), A(3, 3) }; // 4. 對(duì)象數(shù)組使用聚合初始化C11 列表初始化 A* p5 new A[3]{ {1, 1}, {2, 2}, {3, 3} }; // 釋放內(nèi)存 delete p1; delete p2; delete[] p3; delete[] p4; delete[] p5; return 0; }具體用法詳解對(duì)單個(gè)對(duì)象class A { public: A(int x) : _a(x) { cout A(int) 構(gòu)造 endl; } A(int x, int y) : _a(x), _b(y) { cout A(int,int) 構(gòu)造 endl; } ~A() { cout ~A() 析構(gòu) endl; } private: int _a; int _b; }; int main() { // 1. 單個(gè)對(duì)象調(diào)用構(gòu)造函數(shù) A* p1 new A(10); // 調(diào)用 A(int) A* p2 new A(20, 30); // 調(diào)用 A(int, int) // 2. 釋放單個(gè)對(duì)象調(diào)用析構(gòu)函數(shù) delete p1; // 先調(diào)用 ~A()再釋放內(nèi)存 delete p2; return 0; }對(duì)象數(shù)組int main() { // 方式1默認(rèn)構(gòu)造需要 A 有默認(rèn)構(gòu)造 A* p1 new A[3]; // 調(diào)用 3 次默認(rèn)構(gòu)造 // 方式2使用已有對(duì)象拷貝構(gòu)造 A a1(1, 1), a2(2, 2), a3(3, 3); A* p2 new A[3]{ a1, a2, a3 }; // 調(diào)用 3 次拷貝構(gòu)造 // 方式3使用臨時(shí)對(duì)象 A* p3 new A[3]{ A(1, 1), A(2, 2), A(3, 3) }; // 方式4列表初始化C11 A* p4 new A[3]{ {1, 1}, {2, 2}, {3, 3} }; // 釋放數(shù)組必須用 delete[] delete[] p1; // 先調(diào)用 3 次析構(gòu)再釋放內(nèi)存 delete[] p2; delete[] p3; delete[] p4; return 0; }關(guān)鍵點(diǎn)——new和delete的底層調(diào)用的是operatror new和operator deletenew的執(zhí)行過程A* p new A(1, 2); // 執(zhí)行步驟 // 1. 調(diào)用 operator new 分配內(nèi)存類似 malloc // 2. 在分配的內(nèi)存上調(diào)用 A 的構(gòu)造函數(shù) // 3. 返回指向該對(duì)象的指針 new A(1, 2) │ ├─ 1. 調(diào)用 operator new (全局函數(shù)) │ │ │ └─ 調(diào)用 malloc() 分配原始內(nèi)存 │ │ │ ├─ 成功 → 返回內(nèi)存地址 │ └─ 失敗 → 執(zhí)行用戶設(shè)置的應(yīng)對(duì)措施 │ └─ 仍失敗 → 拋出 bad_alloc 異常 │ ├─ 2. 在分配的內(nèi)存上調(diào)用 A 的構(gòu)造函數(shù) │ └─ A::A(1, 2) 初始化對(duì)象 │ └─ 3. 返回指向構(gòu)造完成的對(duì)象的指針delete的執(zhí)行過程delete p; // 執(zhí)行步驟 // 1. 調(diào)用 p 指向?qū)ο蟮奈鰳?gòu)函數(shù) // 2. 調(diào)用 operator delete 釋放內(nèi)存類似 free delete p │ ├─ 1. 調(diào)用 p 指向?qū)ο蟮奈鰳?gòu)函數(shù) │ └─ A::~A() 清理對(duì)象資源 │ └─ 2. 調(diào)用 operator delete (全局函數(shù)) │ └─ 調(diào)用 free() 釋放原始內(nèi)存new[]和delete[]的執(zhí)行過程A* p new A[3]{ A(1,1), A(2,2), A(3,3) }; // 執(zhí)行步驟 // 1. 分配能容納 3 個(gè) A 對(duì)象的內(nèi)存 額外空間存儲(chǔ)數(shù)組大小 // 2. 按順序調(diào)用 3 次構(gòu)造函數(shù) delete[] p; // 執(zhí)行步驟 // 1. 按逆序調(diào)用 3 次析構(gòu)函數(shù)先析構(gòu)最后一個(gè) // 2. 釋放內(nèi)存注意事項(xiàng)new和delete的配對(duì)// 錯(cuò)誤 A* p1 new A(1); delete[] p1; // 錯(cuò)誤單個(gè)對(duì)象不能用 delete[] A* p2 new A[3]; delete p2; // 錯(cuò)誤數(shù)組必須用 delete[] // 正確 A* p1 new A(1); delete p1; A* p2 new A[3]; delete[] p2;自定義類型必須要有正確的構(gòu)造函數(shù)class B { public: B(int x) : _x(x) {} // 只有帶參構(gòu)造沒有默認(rèn)構(gòu)造 private: int _x; }; int main() { B* p1 new B(10); // 可以 // B* p2 new B; // 錯(cuò)誤沒有默認(rèn)構(gòu)造 // B* p3 new B[3]; // 錯(cuò)誤數(shù)組需要默認(rèn)構(gòu)造 delete p1; return 0; }內(nèi)存泄漏問題/ 內(nèi)存泄漏 void BadFunction() { A* p new A(1); // 沒有 delete p函數(shù)結(jié)束導(dǎo)致內(nèi)存泄漏 } // 正確 void GoodFunction() { A* p new A(1); delete p; } // 更好使用智能指針 #includememory void BestFunction() { unique_ptrA p make_uniqueA(1); // 自動(dòng)釋放 }關(guān)于operator new 和 operator deletevoid* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { void* p; // 嘗試分配內(nèi)存 while ((p malloc(size)) 0) { // 如果分配失敗執(zhí)行用戶設(shè)置的 new_handler if (_callnewh(size) 0) { // 用戶沒有設(shè)置應(yīng)對(duì)措施或措施失敗拋出異常 static const std::bad_alloc nomem; _RAISE(nomem); } } return p; } void operator delete(void* pUserData) { if (pUserData NULL) return; // 直接調(diào)用 free 釋放內(nèi)存 free(pUserData); }核心newoperator new分配內(nèi)存構(gòu)造函數(shù)delete析構(gòu)函數(shù)operator delete釋放內(nèi)存new[]operator new[]分配內(nèi)存N次構(gòu)造函數(shù)delete[]N次析構(gòu)函數(shù)operator delete[]釋放內(nèi)存