今天我们来聊聊 C 语言里最让人头秃,但也最硬核的部分------动态内存管理。
你是否经常遇到这样的困扰:在代码中定义了一个数组,运行时才发现空间预留不足;或者为了避免空间不足而过度分配,导致大量内存被闲置浪费,甚至因此被批评"资源利用率低"?
在 C 语言的传统编程方式中,变量和数组的内存空间通常在编译期间就已固定分配(即静态内存分配)。这就像去餐厅就餐,必须提前预订固定人数的座位------人数超出则无法容纳,人数不足则座位空置,造成浪费。
而动态内存管理则提供了另一种思路:它允许程序在运行时,根据需要随时向"堆区"这块公共内存池申请空间。就像在美食广场临时租用摊位,需要多大就租多大,用完即可归还。这种方式听起来十分灵活,但其中也暗藏风险,稍有不慎便可能导致内存泄漏或程序崩溃。
今天咱们就把 malloc, free, calloc, realloc 这四个"房产中介"彻底聊透。
一、为什么要动态内存?静态内存太"死板"
先看看以前的老路子:

局限性很明显:
-
空间开死了:数组长度 10 就是 10,运行的时候你想变成 11?没门。
-
内存浪费 :你不知道用户到底要输入多少数据,为了保险,你开了个
arr[10000],结果用户只输了 5 个数,剩下那 9995 个空间都在"睡大觉"。
这时候,动态内存分配就闪亮登场了。它让程序在运行时,根据需求去申请空间,灵活得像条泥鳅。
二、四大金刚:内存界的"租房"指南
1. malloc:最原始的"毛坯房"
malloc 是动态内存的入门券。

-
功能:去堆区申请一块连续的空间。
-
参数 :
size是你想申请的字节数。 -
返回值 :成功给地址,失败给
NULL。
eg:

这就像你去租了个毛坯房(只有空间,里面啥也没有)。
注意 :malloc 不会帮你初始化,里面装的是之前住户留下的"垃圾数据"(乱码)。
避坑指南:
永远不要假设 malloc 一定成功!如果内存不够了,它会返回 NULL。如果你直接对 NULL 解引用,程序直接给你表演一个"当场去世"(Segmentation Fault)。

2. free:拆迁队,必须得请!
有借有还,再借不难。动态申请的内存,不用了必须手动释放。

-
如果
ptr是动态开辟的,free把它还给操作系统。 -
如果
ptr是NULL,free啥也不干(很安全)。 -
大坑警告 :如果
ptr不是动态开辟的(比如栈上的普通变量),free会直接搞崩程序。
正确姿势:

malloc和free必须成对出现
3. calloc:自带"装修"的精装房
如果你嫌 malloc 里的垃圾数据太乱,那就用 calloc。

- 功能 :申请
num个大小为size的空间,并且把每个字节都初始化为 0。

这就好比你租了个房,房东不仅给了钥匙,还把地拖干净了,床铺好了。适合强迫症患者。
4. realloc:想换房?找它!
这是最秀的一个函数。你住着住着觉得房子太小了,或者突然失业觉得房子太大了,realloc 帮你原地扩建 或者缩容。

-
ptr:原来的地址。 -
size:新的大小(字节)。
它的两种操作模式(重点!):
-
模式一(原地扩):原来的房子后面还有空地,直接把墙拆了往后接一间。地址不变,数据还在。
-
模式二(搬家) :原来的房子后面没地儿了。中介(系统)会去别处找一套更大的新房,把你的家具(数据)搬过去,然后把旧房子拆了。这时候地址就变了!
大部分人会犯的错误:

如果 realloc 搬家失败了,它会返回 NULL。如果你直接赋值给 ptr,那你原来的地址丢了,数据也没了,直接内存泄漏。
正确姿势:

三、千万别踩这些雷!动态内存的"死亡陷阱"
这部分全是面试和实战中的高频翻车点,建议全文背诵。
雷区1:对 NULL 指针动手

对策:先检查,再使用。
雷区2:越界访问(把墙凿穿了)

你只租了 10 个 int 的地盘,非要往第 11 个里面塞东西,这就是在隔壁邻居家里乱涂乱画,系统会直接把你赶出去(程序崩溃)。
雷区3:释放非动态内存

雷区4:只释放一部分(把房产证撕了一半)

对策:谁申请,谁释放;申请时的指针长啥样,释放时就得长啥样。
雷区5:重复释放(拆迁队来两次)

对策 :free(p) 之后立马 p = NULL。因为 free(NULL) 是安全的。
雷区6:忘记释放(内存泄漏)

这就好比你租了房,住完不退房,也不交钥匙,房东(操作系统)收不回房子,最后房子全被你占光了,系统就卡死了。
四、经典笔试题:看看你掉坑里没?
题目1:为什么没输出?

解析 :C 语言是值传递。你想在子函数里改指针,必须传指针的地址(二级指针)。
题目2:野指针乱入

解析 :p 是栈上的数组,函数结束,这块内存就被回收了。你拿到的地址虽然还在,但里面的内容已经不是"hello"了,可能是乱码。
题目3:释放后接着用

解析 :free 只是告诉系统这块地我不用了,但指针 str 的值没变(还是那个地址)。这时候它是个野指针 。所以一定要 str = NULL。
五、总结时间到!!!
动态内存管理是 C 语言的灵魂,也是最难驾驭的部分。咱们最后总结一下:
| 函数 | 外号 | 核心特点 | 注意事项 |
|---|---|---|---|
| malloc | 毛坯房 | 申请空间,不初始化 | 必须检查返回值是否为 NULL |
| free | 拆迁队 | 释放空间 | 只能 free 堆上的,且不能重复 free |
| calloc | 精装房 | 申请并清零 | 适合不想看到乱码的场景 |
| realloc | 换房中介 | 调整空间大小 | 一定要用新指针接返回值 |
重要记忆:
申请检查 NULL,用完记得 free 掉,free 之后置 NULL,指针传递用地址。