【C++特殊工具與技術】優化內存分配(一):C++中的內存分配

目錄

一、C++ 內存的基本概念?

1.1 內存的物理與邏輯結構?

1.2 C++ 程序的內存區域劃分?

二、棧內存分配?

2.1 棧內存的特點?

2.2 棧內存分配示例?

三、堆內存分配?

3.1 new和delete操作符?

4.2 內存泄漏與懸空指針問題?

4.3 new和delete的重載?

四、智能指針與動態內存管理?

4.1 智能指針的概念?

4.2 std::unique_ptr?

4.3 std::shared_ptr?

4.4 std::weak_ptr?

五、總結?


在 C++ 編程中,內存管理是一個至關重要的環節。合理的內存分配和管理不僅能提高程序的性能,還能避免諸如內存泄漏、懸空指針等嚴重問題。C++ 提供了多種內存分配方式,從基礎的棧內存分配到靈活的堆內存分配,每種方式都有其特點和適用場景。

一、C++ 內存的基本概念?

1.1 內存的物理與邏輯結構?

在計算機系統中,物理內存是實際的硬件存儲設備,用于存儲程序運行時的數據和指令。而邏輯內存則是操作系統為每個進程提供的一個抽象的內存空間視圖,它使得每個進程都認為自己擁有整個系統內存。操作系統通過內存管理單元(MMU)將邏輯地址轉換為物理地址,實現內存的高效管理和保護。?

1.2 C++ 程序的內存區域劃分?

C++ 程序在運行時,其內存空間通常被劃分為以下幾個區域:?

  • 棧(Stack):棧是一塊連續的內存區域,由編譯器自動管理。主要用于存儲局部變量、函數參數、返回地址等。棧的分配和釋放速度非常快,遵循后進先出(LIFO)的原則。?
  • 堆(Heap):堆是一塊不連續的內存區域,用于動態內存分配。程序員通過new和delete操作符在堆上分配和釋放內存。堆內存的管理相對復雜,容易出現內存泄漏等問題。?
  • 全局 / 靜態存儲區:用于存儲全局變量和靜態變量。該區域在程序啟動時分配,程序結束時釋放。全局變量和靜態變量根據初始化情況,又分為初始化的全局 / 靜態存儲區和未初始化的全局 / 靜態存儲區(BSS 段) 。?
  • 常量存儲區:用于存儲常量,如字符串常量。常量存儲區的內容在程序運行期間是只讀的。?

可以用下圖來直觀展示 C++ 程序的內存區域劃分:

嵌入式C語言:內存管理_嵌入式內存管理-CSDN博客?

二、棧內存分配?

2.1 棧內存的特點?

棧內存具有以下特點:?

  • 自動管理:棧內存的分配和釋放由編譯器自動完成,無需程序員手動干預。?
  • 速度快:由于棧的操作遵循后進先出原則,并且是在連續的內存區域進行操作,所以棧內存的分配和釋放速度非常快。?
  • 大小有限:棧的大小在程序運行前通常是固定的,不同的操作系統和編譯器對棧的大小限制不同。如果函數調用層級過深,或者局部變量占用空間過大,可能會導致棧溢出。?

2.2 棧內存分配示例?

下面通過一個簡單的 C++ 代碼示例來展示棧內存的分配和使用:?

#include <iostream>void function() {int localVar = 10;  // 局部變量 localVar 在棧上分配內存std::cout << "Local variable in function: " << localVar << std::endl;
}int main() {int mainVar = 20;  // 局部變量 mainVar 在棧上分配內存std::cout << "Local variable in main: " << mainVar << std::endl;function();return 0;
}

mainVar和localVar都是局部變量,它們在函數調用時在棧上分配內存,函數結束時,棧內存會自動釋放。?

三、堆內存分配?

3.1 new和delete操作符?

在 C++ 中,使用new操作符在堆上分配內存,使用delete操作符釋放堆內存。new操作符返回一個指向分配內存的指針,delete操作符用于釋放new分配的內存。?

①基本數據類型的堆內存分配?

#include <iostream>int main() {int* ptr = new int;  // 在堆上分配一個 int 類型的內存空間*ptr = 10;  // 向分配的內存空間寫入數據std::cout << "Value in heap memory: " << *ptr << std::endl;delete ptr;  // 釋放堆內存return 0;
}

②數組的堆內存分配?

#include <iostream>int main() {int* arr = new int[5];  // 在堆上分配一個包含 5 個 int 元素的數組for (int i = 0; i < 5; ++i) {arr[i] = i;}for (int i = 0; i < 5; ++i) {std::cout << "arr[" << i << "]: " << arr[i] << std::endl;}delete[] arr;  // 釋放堆上的數組內存return 0;
}

需要注意的是,在釋放數組內存時,必須使用delete[],否則可能會導致內存泄漏或程序崩潰。?

4.2 內存泄漏與懸空指針問題?

①內存泄漏:當使用new分配的內存沒有通過delete釋放時,就會發生內存泄漏。隨著程序的運行,內存泄漏會導致可用內存逐漸減少,最終可能導致程序性能下降甚至崩潰。

#include <iostream>void memoryLeak() {int* ptr = new int;// 沒有調用 delete ptr,導致內存泄漏
}int main() {memoryLeak();return 0;
}

②懸空指針:當通過delete釋放了堆內存后,如果沒有將指針設置為nullptr,該指針就會成為懸空指針。使用懸空指針進行解引用操作會導致未定義行為。?

#include <iostream>int main() {int* ptr = new int;*ptr = 10;delete ptr;// 此時 ptr 成為懸空指針std::cout << *ptr << std::endl;  // 未定義行為return 0;
}

為了避免懸空指針問題,可以在釋放內存后將指針設置為nullptr:

#include <iostream>int main() {int* ptr = new int;*ptr = 10;delete ptr;ptr = nullptr;  // 將指針設置為 nullptrreturn 0;
}

4.3 new和delete的重載?

在 C++ 中,可以重載new和delete操作符,以實現自定義的內存分配策略。例如,可以實現內存池來提高內存分配的效率,減少內存碎片。?

#include <iostream>
#include <cstdlib>class MemoryPool {
private:static const size_t POOL_SIZE = 1024;  // 內存池大小char* pool;size_t current;public:MemoryPool() : pool(static_cast<char*>(std::malloc(POOL_SIZE))), current(0) {}~MemoryPool() { std::free(pool); }void* allocate(size_t size) {if (POOL_SIZE - current < size) {return std::malloc(size);  // 內存池不足時,使用標準 malloc}void* result = pool + current;current += size;return result;}void deallocate(void* ptr) {// 簡單實現,不支持真正的釋放回內存池,僅標記為可分配if (ptr >= pool && ptr < pool + POOL_SIZE) {// 這里可以添加更復雜的標記邏輯} else {std::free(ptr);}}
};MemoryPool globalPool;void* operator new(size_t size) {return globalPool.allocate(size);
}void operator delete(void* ptr) noexcept {globalPool.deallocate(ptr);
}class MyClass {
public:int data;MyClass() : data(0) {}
};int main() {MyClass* obj = new MyClass;obj->data = 10;std::cout << "Data in MyClass: " << obj->data << std::endl;delete obj;return 0;
}

通過重載new和delete操作符,實現了一個簡單的內存池。當分配內存時,優先從內存池中獲取,如果內存池不足,則使用標準的malloc函數。釋放內存時,對于從內存池中分配的內存,簡單標記為可分配(實際應用中可實現更復雜的回收邏輯),對于使用malloc分配的內存,則使用free釋放。?

四、智能指針與動態內存管理?

4.1 智能指針的概念?

智能指針是 C++ 標準庫提供的一種用于自動管理動態內存的類模板。它通過封裝原始指針,并在適當的時候自動釋放所指向的內存,從而避免了手動管理內存時容易出現的內存泄漏和懸空指針問題。?

4.2 std::unique_ptr?

std::unique_ptr是一種獨占所有權的智能指針,它不支持拷貝構造和賦值操作,確保每個std::unique_ptr實例都唯一地擁有所指向的對象。當std::unique_ptr對象被銷毀時,它所指向的內存會自動釋放。?

#include <iostream>
#include <memory>class MyClass {
public:MyClass() { std::cout << "MyClass constructor" << std::endl; }~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};int main() {std::unique_ptr<MyClass> ptr(new MyClass);  // 創建 std::unique_ptr// ptr 離開作用域時,MyClass 對象的內存會自動釋放return 0;
}

std::unique_ptr還提供了release和reset等成員函數,用于轉移所有權和釋放當前指向的對象。?

4.3 std::shared_ptr?

std::shared_ptr是一種共享所有權的智能指針,多個std::shared_ptr可以指向同一個對象,通過引用計數來管理對象的生命周期。當最后一個指向對象的std::shared_ptr被銷毀時,對象的內存會自動釋放。?

#include <iostream>
#include <memory>class MyClass {
public:MyClass() { std::cout << "MyClass constructor" << std::endl; }~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};int main() {std::shared_ptr<MyClass> ptr1(new MyClass);  // 創建 std::shared_ptrstd::shared_ptr<MyClass> ptr2 = ptr1;  // 共享所有權,引用計數加 1// 當 ptr1 和 ptr2 都離開作用域時,MyClass 對象的內存會自動釋放return 0;
}

std::shared_ptr還提供了use_count等成員函數,用于獲取當前對象的引用計數。?

4.4 std::weak_ptr?

std::weak_ptr是一種弱引用的智能指針,它不影響對象的生命周期,主要用于解決std::shared_ptr循環引用的問題。std::weak_ptr不能直接解引用,需要先通過lock函數將其轉換為std::shared_ptr。?

#include <iostream>
#include <memory>class B;class A {
public:std::weak_ptr<B> ptrB;~A() { std::cout << "A destructor" << std::endl; }
};class B {
public:std::weak_ptr<A> ptrA;~B() { std::cout << "B destructor" << std::endl; }
};int main() {std::shared_ptr<A> a(new A);std::shared_ptr<B> b(new B);a->ptrB = b;b->ptrA = a;// 當 a 和 b 離開作用域時,對象 A 和 B 的內存會正常釋放return 0;
}

通過std::weak_ptr打破了A和B之間的循環引用,避免了內存泄漏。?

五、總結?

本文詳細介紹了 C++ 中的內存分配相關知識,包括棧內存和堆內存的分配方式、new和delete操作符的使用、內存泄漏和懸空指針問題,以及智能指針在動態內存管理中的應用。合理選擇和使用內存分配方式,正確處理內存管理問題,對于編寫高效、穩定的 C++ 程序至關重要。在實際編程中,應根據具體需求選擇合適的內存管理策略,充分利用 C++ 提供的內存管理工具,提高程序的質量和性能。?


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83886.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83886.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83886.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

DeepSeek 賦能智慧能源:微電網優化調度的智能革新路徑

目錄 一、智慧能源微電網優化調度概述1.1 智慧能源微電網概念1.2 優化調度的重要性1.3 目前面臨的挑戰 二、DeepSeek 技術探秘2.1 DeepSeek 技術原理2.2 DeepSeek 獨特優勢2.3 DeepSeek 在 AI 領域地位 三、DeepSeek 在微電網優化調度中的應用剖析3.1 數據處理與分析3.2 預測與…

Redis配合唯一序列號實現接口冪等性方案

1.原理 可以在客戶端每次請求服務端的時候&#xff0c;客戶端請求中攜帶一個短時間內唯一不重復的序列號來確保其唯一性&#xff0c;這個序列號常見的幾種形式有&#xff1a;基于時間戳、用戶ID和隨機數的組合&#xff1b;基于請求的來源與客戶端生成的唯一序列號組合 2.方案…

代碼安全規范1.1

命令注入是指應用程序執行命令的字符串或字符串的一部分來源于不可信賴的數據源&#xff0c;程序沒有對這 些不可信賴的數據進行驗證、過濾&#xff0c;導致程序執行惡意命令的一種攻擊方式。 例 1 &#xff1a;以下代碼通過 Runtime.exec() 方法調用 Windows 的 dir 命…

Jenkins實現自動化部署Springboot項目到Docker容器(Jenkinsfile)

Jenkins實現自動化部署Springboot項目到Docker容器 引言:為什么需要自動化部署? 在軟件開發中,頻繁的手動部署既耗時又容易出錯。通過 Docker + Jenkins + Git 的組合,您可以實現: ? 一鍵部署:代碼推送后自動構建和部署?? 環境一致性:Docker 確保開發、測試、生產環…

第二屆智慧教育與計算機技術國際學術會議(IECT 2025)

在數字化浪潮中&#xff0c;智慧教育與計算機技術的深度融合正重構教育生態。智能教學系統打破傳統課堂的單向灌輸模式&#xff0c;通過機器學習分析學習數據&#xff0c;為學生生成個性化學習路徑&#xff0c;推動被動接受向主動探索轉型。這對教育體系提出核心訴求&#xff1…

驅控邊界在哪里?知名舵機品牌偉創動力CNTE2025展帶來答案

2025年6月12日&#xff0c;北京國防科技裝備展將再度啟幕。作為微型驅控領域的代表性廠商&#xff0c;偉創動力&#xff08;Kpower&#xff09;將帶來覆蓋舵機、減速齒輪箱、無刷電機及一體化驅控模組在內的全系解決方案&#xff0c;舵機產品回應一個至關重要的技術命題——“國…

Day46 Python打卡訓練營

知識點回顧&#xff1a; 1. 不同CNN層的特征圖&#xff1a;不同通道的特征圖 2. 什么是注意力&#xff1a;注意力家族&#xff0c;類似于動物園&#xff0c;都是不同的模塊&#xff0c;好不好試了才知道。 3. 通道注意力&#xff1a;模型的定義和插入的位置 4. 通道注意力后…

專業級PDF轉CAD解決方案

PDF 文件因其出色的便攜性和穩定性&#xff0c;已成為許多用戶的首選格式。但在涉及圖像編輯或精細調整時&#xff0c;CAD 文件顯然更具優勢。 這款 CAD 圖紙轉換工具&#xff0c;界面清爽、操作直觀&#xff0c;是處理圖紙文件的理想助手。 它不僅支持不同版本 CAD 文件之間…

PDF文件如何轉換格式?簡單教程來了

PDF 格式以其高兼容性和穩定性被廣泛使用&#xff0c;但有時為了便于編輯或滿足特定軟件的要求&#xff0c;我們需要將其轉換為其他格式&#xff0c;如 Word、Excel、圖片等。那如何將PDF轉換成其他格式文件呢&#xff1f;其實方法很簡單&#xff0c;不清楚的小伙伴一起來看看吧…

三十四、面向對象底層邏輯-SpringMVC九大組件之FlashMapManager接口設計哲學

在構建符合 RESTful 原則或追求用戶體驗流暢性的 Web 應用時&#xff0c;“重定向后刷新”&#xff08;PRG - Post/Redirect/Get&#xff09;模式是避免表單重復提交、實現頁面無刷新跳轉的黃金法則。然而&#xff0c;重定向&#xff08;REDIRECT:&#xff09;的本質是客戶端發…

android手勢創建及識別保姆級教程

手勢交互&#xff0c;簡單來說&#xff0c;就是通過手指在屏幕上的滑動、點擊、縮放等動作與設備溝通的方式&#xff0c;早已成為現代移動設備用戶體驗的核心支柱。想想看&#xff0c;無論是日常刷短視頻時的上下滑動&#xff0c;還是地圖導航時的雙指縮放&#xff0c;甚至是游…

Python | Windows11通過離線方式安裝pyserial

導言 因公司網絡訪問的限制&#xff0c;沒辦法使用pip install pyserial輕松地安裝pyserial庫。 打開網頁&#xff1a;https://pypi.org/project/pyserial/#files 下載.whl cmd命令行 如下是命令行指令&#xff1a; pip install .\pyserial-3.5-py2.py3-none-any.whlpython …

【nano與Vim】常用命令

使用nano編輯器 保存文件 &#xff1a; 按下CtrlO組合鍵&#xff0c;然后按Enter鍵確認文件名。 退出編輯器 &#xff1a; 按下CtrlX組合鍵。 使用vi或vim編輯器 保存文件 &#xff1a; 按Esc鍵退出插入模式&#xff0c;然后輸入:w并按Enter鍵保存文件。 退出編輯器 &#xf…

(Python網絡爬蟲);抓取B站404頁面小漫畫

目錄 一. 分析網頁 二. 準備工作 三. 實現爬蟲 1. 抓取工作 2. 分析工作 3. 拼接主函數&運行結果 四. 完整代碼清單 1.多線程版本spider.py&#xff1a; 2.異步版本async_spider.py&#xff1a; 經常逛B站的同志們可能知道&#xff0c;B站的404頁面做得別具匠心&…

實戰設計模式之模板方法模式

概述 模板方法模式定義了一個操作中的算法骨架&#xff0c;并將某些步驟延遲到子類中實現。模板方法使得子類可以在不改變算法結構的前提下&#xff0c;重新定義算法中的某些步驟。簡單來說&#xff0c;就是在一個方法中定義了要執行的步驟順序或算法框架&#xff0c;但允許子類…

ROS1: 使用rosbag的方式將點云topic保存為pcd文件

ROS1: 使用rosbag的方式將點云topic保存為pcd文件。 分為兩步&#xff1a;步驟1&#xff1a;通過rosbag錄制點云 &#xff0c;步驟2&#xff1a;通過ros1將rosbag保存為點云pcd文件。 ------------------------ 步驟一&#xff1a;指令示例如下&#xff1a; # topic 名稱&a…

MySQL 高級學習篇

一、連結&#xff08;Join&#xff09; 1.1 概念 聯結&#xff08;Join&#xff09;操作用于將多個表中的列組合在一起&#xff0c;形成一個新的查詢結果集。它允許我們從多個表中提取數據&#xff0c;并基于表之間的關系進行查詢。 1.2 類型 1. 內聯結&#xff08;INNER J…

clickhouse 學習總結

在 ClickHouse 中&#xff0c;配置文件通常位于 /etc/clickhouse 目錄下。這個目錄包含了多個配置文件&#xff0c;用于控制 ClickHouse 的各種服務&#xff08;如服務器、用戶、遠程服務等&#xff09;的配置。 數據存儲目錄/var/lib/clickhouse 配置 文件目錄 /etc/clickho…

理解JavaScript中map和parseInt的陷阱:一個常見的面試題解析

前言 在JavaScript面試中&#xff0c;map和parseInt的組合常常被用作考察候選人對這兩個方法理解深度的題目。讓我們通過一個簡單的例子來深入探討其中的原理。 問題現象 [1, 2, 3].map(parseInt) // 輸出結果是什么&#xff1f;很多人可能會預期輸出[1, 2, 3]&#xff0c;但…

字符串 金額轉換

package heima.Test09;import java.util.Scanner;public class Money {public static void main(String[] args) {//1。鍵盤錄入一個金額Scanner sc new Scanner(System.in);//請輸入一個數據String result "";int money;while (true) {System.out.println("請…