
🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录
- [C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨](#C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨)
-
- [前景回顾:静态内存的局限性 📝](#前景回顾:静态内存的局限性 📝)
- [一、为什么需要动态内存分配? 🤔](#一、为什么需要动态内存分配? 🤔)
- [二、核心函数1:`malloc`------动态内存申请的主力军 🚀](#二、核心函数1:
malloc——动态内存申请的主力军 🚀) -
- [1. `malloc` 函数语法](#1.
malloc函数语法) - [2. `malloc` 的使用步骤(必看!)](#2.
malloc的使用步骤(必看!)) -
- 步骤1:申请内存
- 步骤2:检查是否申请成功(重中之重!)
- 步骤3:使用内存
- [步骤4:释放内存------`free` 函数登场 🧹](#步骤4:释放内存——
free函数登场 🧹) -
- [`free` 函数语法](#
free函数语法) - 释放内存代码
- [`free` 函数语法](#
- [步骤5:将指针置空------防止野指针 🚫](#步骤5:将指针置空——防止野指针 🚫)
- [3. `malloc` 完整使用示例](#3.
malloc完整使用示例)
- [1. `malloc` 函数语法](#1.
- [三、核心函数2:`calloc`------申请内存并自动初始化 🌟](#三、核心函数2:
calloc——申请内存并自动初始化 🌟) -
- [1. `calloc` 函数语法](#1.
calloc函数语法) - [2. `calloc` 的使用示例](#2.
calloc的使用示例) - [3. `malloc` vs `calloc`------核心区别对比表](#3.
mallocvscalloc——核心区别对比表)
- [1. `calloc` 函数语法](#1.
- [四、核心函数3:`realloc`------动态调整内存大小 📏](#四、核心函数3:
realloc——动态调整内存大小 📏) -
- [1. `realloc` 函数语法](#1.
realloc函数语法) - [2. `realloc` 调整内存的两种情况(底层原理)](#2.
realloc调整内存的两种情况(底层原理)) - [3. `realloc` 的正确使用步骤](#3.
realloc的正确使用步骤) -
- 步骤1:先申请一块初始内存
- [步骤2:用新指针接收 `realloc` 的返回值](#步骤2:用新指针接收
realloc的返回值) - 步骤3:使用扩容后的内存
- 步骤4:释放内存并置空
- [4. `realloc` 的两个小技巧](#4.
realloc的两个小技巧)
- [1. `realloc` 函数语法](#1.
- [五、`free` 函数的关键注意事项 🚨](#五、
free函数的关键注意事项 🚨) - [写在最后 📝](#写在最后 📝)
C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨
在之前的学习中,我们掌握了结构体、联合体、枚举等自定义类型,但它们的内存大小都是固定的。而在实际开发中,我们常常需要在程序运行时按需申请和释放内存 ,这就需要用到C语言的动态内存管理功能!这一篇我们从动态内存的必要性出发,详解 malloc、calloc、realloc、free 四大函数的用法、区别和注意事项,帮你打好动态内存的基础!
前景回顾:静态内存的局限性 📝
回顾之前的内存分配方式,我们能清晰看到静态内存的短板:
- 变量/数组的内存大小固定:声明时必须指定大小,且运行时无法调整。
- 联合体共用内存节省空间,但成员大小依然是编译期确定的。
- 结构体内存对齐 是为了提升访问效率,同样无法动态改变大小。
而动态内存管理的核心,就是让程序在运行时自主申请、使用、释放内存,灵活应对多变的需求。
一、为什么需要动态内存分配? 🤔
我们先看两个静态内存的痛点:
- 痛点1 :声明数组时必须指定长度,比如
int arr[10];,如果程序运行时需要存储20个数据,数组就不够用了。 - 痛点2 :如果提前声明一个超大数组(如
int arr[10000];),但实际只用到10个元素,会造成大量内存浪费。
动态内存分配完美解决了这些问题:程序运行时按需申请空间,不用时及时释放,极大提升了内存的利用率。
二、核心函数1:malloc------动态内存申请的主力军 🚀
malloc 是C语言最基础的动态内存申请函数,全称是 memory allocation(内存分配)。
1. malloc 函数语法
c
#include <stdlib.h> // 必须包含的头文件
void* malloc(size_t size);
- 参数
size:要申请的内存字节数(size_t是无符号整数类型)。 - 返回值 :
- 申请成功:返回指向申请到的内存空间的**
void*指针**,需要强制类型转换后使用。 - 申请失败:返回
NULL指针(比如申请的内存过大超出系统限制)。
- 申请成功:返回指向申请到的内存空间的**
2. malloc 的使用步骤(必看!)
使用 malloc 必须遵循"申请→检查→使用→释放→置空"五步走,缺一不可!
步骤1:申请内存
c
// 申请40字节(存放10个int类型数据)
int* p = (int*)malloc(10 * sizeof(int));
💡 推荐写法:10 * sizeof(int) 比直接写 40 更具可移植性(不同平台int大小可能不同)。
步骤2:检查是否申请成功(重中之重!)
malloc 可能申请失败(返回 NULL),如果直接解引用 NULL 指针,程序会崩溃!
c
if (p == NULL)
{
perror("malloc"); // 打印错误原因
return 1; // 异常退出程序
}
极端测试:申请超大内存,必然失败
c
int* p = (int*)malloc(INT_MAX * 100); // INT_MAX是int的最大值
// 运行报错:malloc: Not enough space
步骤3:使用内存
申请到的内存是连续的,可以像数组一样使用指针访问:
c
// 存入1~10
for (int i = 0; i < 10; i++)
{
*(p + i) = i + 1; // 等价于 p[i] = i+1
}
// 打印1~10
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
步骤4:释放内存------free 函数登场 🧹
动态内存不会自动释放,必须用 free 函数手动回收,否则会导致内存泄漏!
free 函数语法
c
void free(void* ptr);
- 参数
ptr:指向要释放的动态内存空间的起始地址。 - 注意事项 :
free只能释放动态开辟的内存 (malloc/calloc/realloc申请的),释放非动态内存会导致未定义行为。- 如果
ptr是NULL指针,free什么都不做(安全操作)。
释放内存代码
c
free(p); // 释放p指向的40字节内存
步骤5:将指针置空------防止野指针 🚫
free 只是释放了内存空间,但指针 p 中还保存着原来的地址,此时 p 变成了野指针 (指向无效内存的指针),如果后续误操作 p,后果不堪设想!
必须做的操作:
c
p = NULL; // 将指针置空,彻底杜绝野指针
3. malloc 完整使用示例
c
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 1. 申请内存
int* p = (int*)malloc(10 * sizeof(int));
// 2. 检查是否成功
if (p == NULL)
{
perror("malloc");
return 1;
}
// 3. 使用内存
for (int i = 0; i < 10; i++)
{
p[i] = i + 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]); // 输出:1 2 3 4 5 6 7 8 9 10
}
// 4. 释放内存
free(p);
// 5. 指针置空
p = NULL;
return 0;
}
三、核心函数2:calloc------申请内存并自动初始化 🌟
calloc 也是动态内存申请函数,全称是 contiguous allocation (连续分配),它和 malloc 的最大区别是:申请内存后自动将每个字节初始化为0。
1. calloc 函数语法
c
#include <stdlib.h>
void* calloc(size_t num, size_t size);
- 参数
num:要申请的元素个数。 - 参数
size:每个元素的字节大小。 - 返回值 :和
malloc一样(成功返回内存指针,失败返回NULL)。
2. calloc 的使用示例
c
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 申请10个int元素的内存,并初始化为0
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
perror("calloc");
return 1;
}
// 打印初始化结果(全是0)
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]); // 输出:0 0 0 0 0 0 0 0 0 0
}
// 释放+置空
free(p);
p = NULL;
return 0;
}
3. malloc vs calloc------核心区别对比表
| 特性 | malloc |
calloc |
|---|---|---|
| 初始化 | 不初始化,内存中是随机值(脏数据) | 自动将每个字节初始化为0 |
| 参数形式 | 直接传总字节数 | 传元素个数+单个元素大小 |
| 效率 | 更高(少了初始化步骤) | 略低(需要初始化内存) |
| 适用场景 | 不需要初始化的场景 | 需要内存初始化为0的场景 |
💡 选择建议:如果需要内存初始化为0,用 calloc;否则用 malloc 更高效。
四、核心函数3:realloc------动态调整内存大小 📏
realloc 函数让动态内存管理更灵活,全称是 re-allocation (重新分配),它可以调整已经申请的内存空间大小(扩大或缩小)。
1. realloc 函数语法
c
#include <stdlib.h>
void* realloc(void* ptr, size_t size);
- 参数
ptr:指向已经申请的动态内存空间的起始地址。 - 参数
size:调整后的新内存大小(字节数)。 - 返回值 :
- 调整成功:返回指向新内存空间的指针。
- 调整失败:返回
NULL指针(原内存空间不受影响)。
2. realloc 调整内存的两种情况(底层原理)
realloc 调整内存时,会根据原有内存的位置和剩余空间,选择两种策略:
| 情况 | 条件 | 操作 | 优点 |
|---|---|---|---|
| 原地扩容 | 原有内存后面有足够的连续空间 | 直接在原有内存后追加空间 | 效率高,原有数据不移动 |
| 异地扩容 | 原有内存后面没有足够空间 | 重新找一块更大的连续内存,将原有数据拷贝过去,释放旧内存 | 保证内存连续,数据不丢失 |
💡 无论哪种情况,都必须用新指针接收 realloc 的返回值 !如果直接用原指针接收,一旦 realloc 失败返回 NULL,会导致原指针变成 NULL,丢失原有内存地址!
3. realloc 的正确使用步骤
步骤1:先申请一块初始内存
c
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("malloc");
return 1;
}
// 初始使用:存入5个5
for (int i = 0; i < 5; i++)
{
p[i] = 5;
}
步骤2:用新指针接收 realloc 的返回值
c
// 调整内存大小为20个int(80字节)
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr == NULL)
{
perror("realloc");
return 1; // 失败时原内存p还能用,不要直接free(p)
}
else
{
p = ptr; // 调整成功,更新p的指向
ptr = NULL; // 新指针置空
}
步骤3:使用扩容后的内存
c
// 新空间存入10个10
for (int i = 5; i < 15; i++)
{
p[i] = 10;
}
// 打印验证
for (int i = 0; i < 15; i++)
{
printf("%d ", p[i]); // 输出:5 5 5 5 5 10 10 ... 10
}
步骤4:释放内存并置空
c
free(p);
p = NULL;
4. realloc 的两个小技巧
-
技巧1 :
realloc可以替代malloc
当ptr为NULL时,realloc(NULL, size)等价于malloc(size)。cint* p = (int*)realloc(NULL, 40); // 等价于 malloc(40) -
技巧2 :缩小内存大小
size可以比原内存小,此时realloc会缩小内存,多余的内存会被释放。
五、free 函数的关键注意事项 🚨
free只释放内存空间,不改变指针的值:所以释放后必须手动将指针置空。- 不能释放动态内存的一部分 :
free的参数必须是动态内存的起始地址,如果指针指向了内存中间位置,释放会出错。 - 不能重复释放同一块内存 :重复释放会导致程序崩溃,释放后置空可以避免这个问题(
free(NULL)是安全的)。 - 程序结束后操作系统会回收动态内存:但这只是"保底措施",不能依赖!如果程序长时间运行(如服务器程序),不手动释放内存会导致内存泄漏,最终耗尽系统资源。
写在最后 📝
malloc、calloc、realloc、free 是动态内存管理的四大核心函数,掌握它们的关键在于:
- 牢记
malloc使用五步走:申请→检查→使用→释放→置空。 - 区分
malloc和calloc:是否需要初始化内存。 - 正确使用
realloc:用新指针接收返回值,失败时保留原内存。 free后必须置空指针:杜绝野指针和重复释放问题。
下一篇我们将深入讲解动态内存的常见错误 和经典笔试题分析,这些都是笔试面试的高频考点,千万不要错过!