C++ 动态内存

C++ 动态内存

一、程序内存分区概述

C++ 程序运行时内存主要分为:栈、堆、全局/静态区、常量区、代码区

日常开发中动态内存管理核心围绕**栈(Stack)堆(Heap)**展开。


二、栈(Stack)详细说明

  1. 管理方式

    由操作系统自动分配、自动回收,无需程序员手动干预。

  2. 存储内容

    函数局部变量、函数形参、临时变量、局部实例化对象。

  3. 生命周期

    绑定函数作用域,函数执行结束、作用域销毁时,栈内存立刻自动释放。

  4. 空间特性

  • 空间容量小、上限固定(系统默认数MB);
  • 内存地址连续,地址由高向低增长;
  • 空间不足会触发栈溢出(Stack Overflow)
  1. 访问效率

    内存连续、CPU寻址高效,读写速度快。

  2. 线程属性

    每个线程拥有独立栈空间,线程私有,不存在多线程竞争问题。


三、堆(Heap)详细说明

  1. 管理方式

    由程序员手动控制,通过 new / new[] 申请,delete / delete[] 手动释放。

  2. 存储内容

    动态变量、动态数组、大型对象、需要跨作用域长期使用的数据。

  3. 生命周期

    不受函数作用域限制,内存分配后会一直常驻,直到手动释放或程序进程结束;

    未手动释放会造成内存泄漏

  4. 空间特性

  • 空间极大,受系统虚拟内存限制,可灵活分配大容量内存;
  • 内存地址不连续,频繁分配释放易产生内存碎片
  • 分配失败返回空指针或抛出异常,无溢出风险。
  1. 访问效率

    分配与释放开销大,碎片化内存导致访问效率低于栈。

  2. 线程属性

    全局共享内存,多线程同时操作堆空间需要加锁保证线程安全。


四、堆与栈核心区别对照表

对比维度 栈(Stack) 堆(Heap)
内存管理者 系统自动管理 程序员手动管理
分配方式 自动分配 new 动态手动分配
释放方式 作用域结束自动释放 必须手动 delete 释放
空间大小 容量小、固定上限 空间大、支持动态扩容
内存布局 连续内存空间 非连续内存,易产生碎片
分配速度 快速高效 分配释放较慢
生命周期 随函数/作用域销毁 生命周期自定义,跨函数可用
典型用途 局部小变量、临时数据 大容量数据、动态数组、长生命周期对象
溢出问题 易发生栈溢出 无溢出,仅分配失败
地址增长方向 高地址 → 低地址 低地址 → 高地址
线程归属 线程私有 进程全局共享

五、C++ 动态内存:new 与 delete

5.1 核心作用

  • new:在堆区手动分配内存,自动调用类的构造函数
  • delete:释放堆区内存,自动调用类的析构函数

5.2 单个内存分配与释放

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
    // 堆上分配单个 double 内存
    double* pvalue = new double;
    *pvalue = 29494.99;

    cout << "堆内存变量值:" << *pvalue << endl;

    // 释放堆内存
    delete pvalue;
    pvalue = nullptr; // 释放后置空,避免野指针
    return 0;
}

5.3 内存分配失败判断

堆内存不足时 new 会分配失败,增加空指针判断保证程序健壮性:

cpp 复制代码
double* p = nullptr;
if (!(p = new double))
{
    cout << "内存不足,分配失败!" << endl;
    exit(1);
}

六、动态数组内存分配

6.1 一维动态数组

cpp 复制代码
// 分配堆数组
int* arr = new int[10];

// 使用数组
for(int i = 0; i < 10; ++i)
    arr[i] = i;

// 数组释放必须使用 delete[]
delete[] arr;
arr = nullptr;

6.2 二维动态数组

cpp 复制代码
// 二维数组开辟
int row = 3, col = 4;
int** mat = new int*[row];
for(int i = 0; i < row; ++i)
{
    mat[i] = new int[col];
}

// 二维数组释放
for(int i = 0; i < row; ++i)
{
    delete[] mat[i];
}
delete[] mat;
mat = nullptr;

6.3 关键规则

  • new[] 开辟数组,必须配套 delete[] 释放;
  • 混用 new / delete[] 会导致内存泄漏、程序崩溃。

七、对象动态内存分配

使用 new / delete 操作类对象,会自动触发构造与析构:

cpp 复制代码
#include <iostream>
using namespace std;

class Box
{
public:
    Box()
    {
        cout << "调用构造函数" << endl;
    }
    ~Box()
    {
        cout << "调用析构函数" << endl;
    }
};

int main()
{
    // 堆上创建 4 个对象,调用 4 次构造
    Box* boxArr = new Box[4];

    // 释放数组对象,调用 4 次析构
    delete[] boxArr;
    boxArr = nullptr;
    return 0;
}

八、new/delete 与 malloc/free 对比

  1. malloc / free
  • C 语言标准库函数;
  • 仅负责原始内存的申请与释放
  • 不调用构造函数、析构函数,不适合 C++ 面向对象开发。
  1. new / delete
  • C++ 专属运算符;
  • 分配内存 + 自动调用构造函数;
  • 释放内存 + 自动调用析构函数;
  • 类型安全,支持重载,C++ 项目优先使用

九、动态内存使用规范与注意事项

  1. 配对原则:new 对应 deletenew[] 对应 delete[],禁止混用;
  2. 内存泄漏:堆内存必须手动释放,长期遗漏会导致程序内存占用持续上涨;
  3. 野指针规避:内存释放后,立即将指针赋值为 nullptr
  4. 禁止重复释放、悬空指针访问已释放内存;
  5. 场景选择:
    • 短期、局部、小数据 → 使用栈内存;
    • 大数据、动态长度、跨作用域 → 使用堆内存;
  6. 多线程开发中,共享堆资源需做好同步加锁。

十、代码示例:栈内存 vs 堆内存

cpp 复制代码
// 栈内存:自动分配自动释放
void stackDemo()
{
    int a = 10;         // 栈变量
    char buf[1024];     // 栈数组
    Box box;            // 栈对象
}
// 函数结束,所有栈变量自动销毁

// 堆内存:手动管理
void heapDemo()
{
    int* pNum = new int(100);
    Box* pBox = new Box;
    int* pArr = new int[20];

    // 手动释放
    delete pNum;
    delete pBox;
    delete[] pArr;
}
相关推荐
Web Security Loop11 分钟前
MAC卸载JDK环境
java·macos·jdk
Rain的Java大神之路1 小时前
线上接口负载满了如何解决
java·数据库·redis·后端·缓存·面试·架构
counting money2 小时前
Tomcat 专题:从基础架构到性能调优与 WebSocket 实战
java·websocket·tomcat
间歇性努力持续性发呆的野生快乐选手2 小时前
栈的性质(进栈,出栈,访问)
数据结构·c++
xiaohaiAIgeo2 小时前
【2026年】HG/T 20656-2024化工暖通空调设计规范:新版标准的变化与影响
java·前端·javascript·科普知识
旖旎夜光2 小时前
LeetCode 904:水果成篮(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
小强库计算机毕业设计3 小时前
基于 Spring Boot + Vue3 的校园管理系统
java·spring boot·后端·项目实战·管理系统·技术分享·校园管理系统实战
霸道流氓气质3 小时前
Java中信号量(Semaphore):从本地到分布式
java·开发语言·分布式
怪奇云呼军3 小时前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
编程令我快乐3 小时前
maven部分构建参数讲解
java·maven