0%
boxmoe_header_banner_img

加载中

内存管理


avatar
mizuki 2026年6月5日 2026年6月5日 40

引言

在C语言的学习中,我们已经初步了解了C语言的内存分布,这篇文章将介绍C++相关的内存管理内容

1.C/C++的内存分布

  • 1.1 题目

在正式开始讲解内存管理前,我们先做一个小测试,检测一下之前的知识还剩了多少()

答案(点击箭头查看)

CCCAA AAADAB

因为大部分的都比较简单,这里就挑几个较难的讲解:

1.pchar3 *pchar3

因为pchar3是一个指针,所以处在栈上,但*pchar3是首元素,首元素‘a’处于常量区,因此*pchar3在常量区

2.ptr1 *ptr1

同样的,ptr1也是一个指针,处于栈上,但ptr1的空间是动态开辟的,因此*ptr1指向的是堆

为了防止还不够清楚,这里再给一张图解释

  • 1.2 各个区域的分布情况

栈:局部变量

堆:新开的,动态申请的数据(malloc,realloc等)

数据段(静态区):全局变量,静态变量

代码段(常量区):可执行的代码,常量

这里给一张图可以更好的理解:

2.C语言中内存的管理方式

  • 2.1 类型

在C语言中,我们常采用malloc,colloc,realloc来进行内存管理

  • 2.2 区别

1.malloc

功能为分配指定字节数的未初始化内存块,失败返回NULL,分配的内容为随机值

用法:int *arr = (int *)malloc(10 * sizeof(int)); // 分配10个int大小的空间

2.calloc

分配指定数量、指定大小的内存块,失败返回NULL,并将内容初始化为0

用法:int *arr = (int *)calloc(10, sizeof(int)); // 分配10个int,全部初始化为0

3.realloc

重新调整已分配内存块的大小,失败返回NULL,前提是在realloc前对象已被初始化,否则将失败

用法:int *new_arr = (int *)realloc(arr, 20 * sizeof(int)); // 扩大到20个int,arr是已存在的对象

3.C++管理内存方式

  • 3.1 new和delete的用法

1.基本用法

int* p1 = new int;
int* p2 = new int[10];

delete p1;
delete[] p2;

要注意的是如果开辟了>=1个空间,delete时要加上[]

2.原始类型初始化用法

int* p1 = new int(0);
int* p2 = new int[5]{1, 2, 3, 4, 5};

delete p1;
delete[] p2;

对于原始类型的初始化,只有一个空间的在类型后接()即可,对于多个空间的,在类型后接{},在括号内填上初始化的数据,没有初始化的位置默认初始化为0

3.自定义类型初始化用法

class A
{
    public:
    A(int a, int b): _a(a), _b(b)
    {}
    private:
    int _a;
    int _b;
}

A* p1 = new A({1, 2});
A* p2 = new A[5]{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};

对于自定义类型也可以进行初始化,但是要求自定义类型要有相对应的构造函数,如果初始化多个数据,要在{}内再加{}进行初始化,其实也就是用到了匿名对象和隐式类型转化的原理(详细见这篇文章:类和对象(下) – 小园得志)具体用法如上所示

  • 3.2 抛异常(new失败)

1.核心概念

抛异常处理是 C++ 用于处理运行时错误的机制,让程序在出错时能够优雅地恢复或退出,而不是直接崩溃

2.执行流程

程序正常执行 → 遇到 throw → 跳转到匹配的 catch → 处理异常 → 继续执行 catch 之后的代码

3.解释+例子

接下来我将结合这个例子,为大家解释抛异常

void deep_function() {
    std::cout << "我要申请超大内存..." << std::endl;

    // 申请 1TB 内存,必然失败
    int* p = new int[1000000000000];

    // ❗ 如果 new 失败,这里不会执行!直接跳到 catch
    std::cout << "申请成功,不会打印这行" << std::endl;
    *p = 10;
}

void middle_function() {
    std::cout << "中间层:准备调用 deep_function" << std::endl;
    deep_function();  // 异常从这里抛出来,middle_function 也不处理
    std::cout << "中间层:调用完成,不会打印这行" << std::endl;
}

int main() {
    try {
        std::cout << "main:开始" << std::endl;
        middle_function();  // 异常一路抛到 main
        std::cout << "main:调用完成,不会打印这行" << std::endl;
    }
    catch (std::bad_alloc& e) {  // ✅ 在这里捕获!
        std::cout << "main:捕获到异常!原因:" << e.what() << std::endl;
    }

    std::cout << "main:程序继续运行,没有崩溃" << std::endl;
    return 0;
}

try内是需要检测的程序,如果try内的代码能够正常运行,那么就会直接跳过catch内的代码继续执行下面的代码,如果try内代码不能够正常运行,则会直接执行catch内的代码,然后执行catch后的代码,对于上述代码来说,内存分配过大一定会失败,当执行到内存分配的那一行代码时,就会直接跳转到catch(try内的剩余代码也将不执行),执行catch内的代码,然后继续执行剩余代码

此代码运行结果如下:

  • 3.3 new和delete的底层原理

1.原始类型

如果new和delete的对象是原始类型,那么new的底层就是malloc,delete的底层就是free

2.自定义类型

对于自定义类型来说,new的底层就是利用malloc开空间,然后调用自定义对应的构造函数初始化delete的底层则是先调用析构函数,再用free清理空间

总结

本来以为内存管理的内容写不了多少的,但是在写的过程中发现有很多没有掌握完全的知识点,就越写越多了,也算是自我提升了吧

上一次更新已经跑远了✨ 计算中...
(‾◡◝) 本内容里的一些消息,可能已经跟不上时间啦~


评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字

插入代码
小园得志

北京时间 (Asia/Shanghai)

后退
前进
刷新
复制
粘贴
全选
删除
返回首页
0%
目录
顶部
底部
📖 文章导读