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;
}
相关推荐
ZhengEnCi1 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
卷无止境3 小时前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境4 小时前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
狼爷19 小时前
吃透 Java Function 接口,搞定 99% 的 Stream 场景
java·函数式编程
祎雪双十Gy1 天前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
小锋java12341 天前
分享一套锋哥原创的SpringBoot4+Vue3宠物领养网站系统
java
郝学胜_神的一滴1 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
考虑考虑1 天前
Java实现hmacsha1加密算法
java·后端·java ee
掉鱼的猫1 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot
plainGeekDev1 天前
广播接收器 → Flow + Lifecycle
android·java·kotlin