CC++ 内存管理 —— 程序的“五脏六腑”在哪里?

🧠 C/C++ 内存管理 ------ 程序的"五脏六腑"在哪里?

💡 你写的每一行代码,背后都有一块内存默默支撑。

学会内存管理,就像学会给程序"体检"和"做手术"!

本章带你搞懂:

✅ 程序运行时,变量到底住在哪里?

malloc/freenew/delete 有什么区别?

✅ 为什么 C++ 要发明 new

✅ 什么是"定位 new"?它有什么用?


1️⃣ C/C++ 的内存分布 ------ 程序的"五大区域"

想象你的程序是一个房子,内存就是它的房间布局:

区域 别名 存什么? 特点
栈(Stack) 堆栈 局部变量、函数参数、返回值 自动分配/释放,向下增长,速度快
堆(Heap) 动态区 malloc / new 申请的空间 手动管理,向上增长,灵活但易出错
数据段(Data Segment) 静态区 全局变量、静态变量 程序启动时初始化,结束时释放
代码段(Text Segment) 常量区 函数代码、字符串常量(如 "abcd" 只读,不可修改
内存映射段 - 动态库、共享内存等 高级用法,暂时了解即可

🧩 小测试:下面这些变量住在哪?

c++ 复制代码
int globalVar = 1;               // 全局变量
static int staticGlobalVar = 1;  // 静态全局变量

void Test() {
    static int staticVar = 1;    // 静态局部变量
    int localVar = 1;            // 普通局部变量
    int num1[10] = {1, 2, 3, 4}; // 局部数组
    char char2[] = "abcd";       // 字符数组(在栈上)
    const char* pChar3 = "abcd"; // 指针在栈,指向常量区
    int* ptr1 = (int*)malloc(sizeof(int) * 4); // 指针在栈,指向堆
}

答案速查表

变量 位置 解释
globalVar C. 数据段 全局变量
staticGlobalVar C. 数据段 静态全局变量
staticVar C. 数据段 静态局部变量(生命周期=整个程序)
localVar A. 栈 普通局部变量
num1 A. 栈 局部数组(自动分配)
char2 A. 栈 数组本身在栈
*char2(即 'a' A. 栈 数组内容也在栈
pChar3(指针) A. 栈 指针变量是局部变量
*pChar3(即 "abcd" 的首字符) D. 代码段 字符串字面量在常量区
ptr1(指针) A. 栈 指针变量在栈
*ptr1(动态内存) B. 堆 malloc 分配的空间在堆

🔍 小技巧:

  • 变量本身在哪?看定义位置(局部→栈,全局/静态→数据段)
  • 变量指向的内容在哪?看怎么分配的"..." → 代码段,malloc/new → 堆)

2️⃣ C 语言的动态内存管理:malloc/calloc/realloc/free

📌 四兄弟介绍:

函数 作用 是否初始化 返回值
malloc(size) 申请 size 字节 ❌ 不初始化(垃圾值) void*
calloc(n, size) 申请 nsize 字节 ✅ 全部初始化为 0 void*
realloc(ptr, new_size) 调整已有内存大小 保留原数据 void*(可能新地址!)
free(ptr) 释放动态内存 - 无返回值

✅ 示例:三兄弟怎么用?

c++ 复制代码
#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    // malloc:不初始化
    int* p1 = (int*)malloc(4 * sizeof(int));
    
    // calloc:初始化为0
    int* p2 = (int*)calloc(4, sizeof(int)); // [0,0,0,0]
    
    // realloc:扩容(注意:可能移动地址!)
    int* p3 = (int*)realloc(p2, 10 * sizeof(int)); // 原数据保留
    
    // 使用...
    for (int i = 0; i < 10; ++i) cout << p3[i] << " "; // 前4个是0,后6个未定义
    
    // 释放
    free(p1);
    free(p3); // 注意:p2 已被 realloc "吞掉",不能再 free(p2)!
    
    return 0;
}

⚠️ 重要面试题
realloc(p, new_size) 后,还能 free(p) 吗?

不能! 如果 realloc 移动了内存,p 已失效,free(p) 会导致崩溃!


3️⃣ C++ 的内存管理:newdelete

C++ 觉得 malloc/free 太原始,于是推出了更智能的 new/delete

✅ 用于内置类型(int、double 等)

c++ 复制代码
// 单个
int* p1 = new int;        // 未初始化(随机值)
int* p2 = new int(10);    // 初始化为10

// 数组
int* p3 = new int[5];     // 5个未初始化的int
int* p4 = new int[5]();   // 5个初始化为0的int(C++11)

// 释放
delete p1;
delete p2;
delete[] p3;  // 数组必须用 delete[]
delete[] p4;

📌 口诀

  • newdelete
  • new[]delete[]
    千万别混用!否则未定义行为!

✅ 用于自定义类型(类对象)

这才是 new/delete真正优势

c++ 复制代码
class A {
public:
    A(int a = 0) : _a(a) {
        cout << "构造 A(" << _a << ") @ " << this << endl;
    }
    ~A() {
        cout << "析构 A(" << _a << ") @ " << this << endl;
    }
private:
    int _a;
};

int main() {
    // malloc:只分配空间,不调构造!
    A* p1 = (A*)malloc(sizeof(A)); // 对象未初始化!危险!

    // new:分配空间 + 调用构造函数!
    A* p2 = new A(10); // 自动调用 A(10)

    // 释放
    free(p1);          // 不调析构!资源可能泄漏!
    delete p2;         // 先调析构,再释放内存!

    // 数组
    A* p3 = new A[3];  // 调用3次默认构造
    delete[] p3;       // 调用3次析构 + 释放

    return 0;
}

💡 核心区别
new/delete自动调用构造/析构函数malloc/free 不会


4️⃣ operator newoperator delete ------ new 的底层实现

你以为 new 是魔法?其实它也是"站在巨人肩膀上"!

  • new 表达式 = 调用 operator new 分配内存 + 调用构造函数
  • delete 表达式 = 调用析构函数 + 调用 operator delete 释放内存

🧱 底层实现(简化版):

c++ 复制代码
// operator new 实际调用 malloc
void* operator new(size_t size) {
    void* p = malloc(size);
    if (!p) throw std::bad_alloc(); // 失败抛异常
    return p;
}

// operator delete 实际调用 free
void operator delete(void* p) {
    free(p);
}

✅ 所以:

  • new 失败 → 抛异常(不是返回 NULL)
  • malloc 失败 → 返回 NULL

5️⃣ newdelete 的实现原理

🔹 内置类型:

  • new intmalloc(sizeof(int))(但失败抛异常)
  • delete pfree(p)

🔹 自定义类型:

new A(10) 做了两件事:
  1. 调用 operator new(sizeof(A)) 申请内存
  2. 在该内存上调用 A(10) 构造函数
delete p 做了两件事:
  1. 调用 p->~A() 析构函数
  2. 调用 operator delete(p) 释放内存
new A[3]
  1. 调用 operator new[](3 * sizeof(A))
  2. 调用 3 次构造函数
delete[] p
  1. 调用 3 次析构函数
  2. 调用 operator delete[]

6️⃣ 定位 new(Placement new)------ 在指定内存上构造对象

💡 场景:你已经有一块内存(比如内存池分配的),想在这块内存上"激活"一个对象。

✅ 语法:

c++ 复制代码
new (地址) 类型(参数);

✅ 示例:

c++ 复制代码
class A {
public:
    A(int a) : _a(a) { cout << "构造 A(" << a << ")" << endl; }
    ~A() { cout << "析构 A(" << _a << ")" << endl; }
private:
    int _a;
};

int main() {
    // 1. 先分配原始内存(不调构造!)
    void* raw = malloc(sizeof(A));

    // 2. 在 raw 上构造 A 对象
    A* p = new (raw) A(42); // 定位 new

    // 3. 使用对象
    // ...

    // 4. 手动调析构(因为内存不是 new 分配的!)
    p->~A();

    // 5. 释放原始内存
    free(raw);

    return 0;
}

🌟 用途 :高性能场景(如游戏引擎、数据库)中的内存池技术


7️⃣ malloc/free vs new/delete ------ 终极对比表

对比项 malloc/free new/delete
类型 C 函数 C++ 操作符
初始化 ❌ 不初始化 ✅ 可初始化(new int(10)
类型安全 ❌ 返回 void*,需强转 ✅ 返回具体类型指针
失败处理 返回 NULL 抛出 std::bad_alloc 异常
自定义类型 ❌ 只分配内存 ✅ 分配 + 调用构造/析构
数组支持 需手动计算大小 new T[N] 自动计算
可重载 ❌ 不可重载 ✅ 可重载 operator new/delete

总结

  • C 项目 → 用 malloc/free
  • C++ 项目 → 优先用 new/delete,尤其是涉及类对象时!

🧠 本章口诀总结

🏠 内存五区记心间

栈存局部快如电,堆上动态要管严,

全局静态数据段,代码常量只读安。

🔧 malloc 原始 new 智能

malloc 只管分内存,new 还会调构造;

free 不理析构事,delete 清理全包办!

⚠️ 匹配使用是铁律

new 配 delete,new[] 配 delete[],

混用等于埋炸弹!

🎯 定位 new 有妙用

内存池中显身手,手动构造析构走!


🌟 动手建议

把文中的代码全部敲一遍,观察输出,你会对内存有"肌肉记忆"!

相关推荐
饕餮怪程序猿1 小时前
A*算法(C++实现)
开发语言·c++·算法
观音山保我别报错1 小时前
列表,元组,字典
开发语言·python
**蓝桉**2 小时前
数组的执行原理,java程序的执行原理
java·开发语言
waeng_luo2 小时前
[鸿蒙2025领航者闯关] 表单验证与用户输入处理最佳实践
开发语言·前端·鸿蒙·鸿蒙2025领航者闯关·鸿蒙6实战·开发者年度总结
高频交易dragon2 小时前
5分钟和30分钟联立进行缠论信号分析
开发语言·python
ULTRA??2 小时前
C/C++函数指针
c语言·开发语言·c++
还没想好取啥名2 小时前
C++11新特性(一)——自动类型推导
开发语言·c++·stl
我是华为OD~HR~栗栗呀2 小时前
华为OD-C面经-23届学院哦
java·c++·python·华为od·华为·面试
xiaozi41202 小时前
Ruey S. Tsay《时间序列分析》Python实现笔记:综合与应用
开发语言·笔记·python·机器学习