C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyX游戏规划

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

  • [C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨](#C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨)
    • [前景回顾:静态内存的局限性 📝](#前景回顾:静态内存的局限性 📝)
    • [一、为什么需要动态内存分配? 🤔](#一、为什么需要动态内存分配? 🤔)
    • [二、核心函数1:`malloc`------动态内存申请的主力军 🚀](#二、核心函数1:malloc——动态内存申请的主力军 🚀)
    • [三、核心函数2:`calloc`------申请内存并自动初始化 🌟](#三、核心函数2:calloc——申请内存并自动初始化 🌟)
      • [1. `calloc` 函数语法](#1. calloc 函数语法)
      • [2. `calloc` 的使用示例](#2. calloc 的使用示例)
      • [3. `malloc` vs `calloc`------核心区别对比表](#3. malloc vs calloc——核心区别对比表)
    • [四、核心函数3:`realloc`------动态调整内存大小 📏](#四、核心函数3:realloc——动态调整内存大小 📏)
      • [1. `realloc` 函数语法](#1. realloc 函数语法)
      • [2. `realloc` 调整内存的两种情况(底层原理)](#2. realloc 调整内存的两种情况(底层原理))
      • [3. `realloc` 的正确使用步骤](#3. realloc 的正确使用步骤)
      • [4. `realloc` 的两个小技巧](#4. realloc 的两个小技巧)
    • [五、`free` 函数的关键注意事项 🚨](#五、free 函数的关键注意事项 🚨)
    • [写在最后 📝](#写在最后 📝)

C 语言动态内存管理入门:malloc/calloc/realloc/free 核心函数详解✨

在之前的学习中,我们掌握了结构体、联合体、枚举等自定义类型,但它们的内存大小都是固定的。而在实际开发中,我们常常需要在程序运行时按需申请和释放内存 ,这就需要用到C语言的动态内存管理功能!这一篇我们从动态内存的必要性出发,详解 malloccallocreallocfree 四大函数的用法、区别和注意事项,帮你打好动态内存的基础!

前景回顾:静态内存的局限性 📝

C 语言联合体与枚举:共用内存 + 常量枚举 + 实战

回顾之前的内存分配方式,我们能清晰看到静态内存的短板:

  1. 变量/数组的内存大小固定:声明时必须指定大小,且运行时无法调整。
  2. 联合体共用内存节省空间,但成员大小依然是编译期确定的。
  3. 结构体内存对齐 是为了提升访问效率,同样无法动态改变大小。

而动态内存管理的核心,就是让程序在运行时自主申请、使用、释放内存,灵活应对多变的需求。

一、为什么需要动态内存分配? 🤔

我们先看两个静态内存的痛点:

  • 痛点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:指向要释放的动态内存空间的起始地址。
  • 注意事项
    1. free 只能释放动态开辟的内存malloc/calloc/realloc 申请的),释放非动态内存会导致未定义行为。
    2. 如果 ptrNULL 指针,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 的两个小技巧

  • 技巧1realloc 可以替代 malloc
    ptrNULL 时,realloc(NULL, size) 等价于 malloc(size)

    c 复制代码
    int* p = (int*)realloc(NULL, 40); // 等价于 malloc(40)
  • 技巧2 :缩小内存大小
    size 可以比原内存小,此时 realloc 会缩小内存,多余的内存会被释放。

五、free 函数的关键注意事项 🚨

  1. free 只释放内存空间,不改变指针的值:所以释放后必须手动将指针置空。
  2. 不能释放动态内存的一部分free 的参数必须是动态内存的起始地址,如果指针指向了内存中间位置,释放会出错。
  3. 不能重复释放同一块内存 :重复释放会导致程序崩溃,释放后置空可以避免这个问题(free(NULL) 是安全的)。
  4. 程序结束后操作系统会回收动态内存:但这只是"保底措施",不能依赖!如果程序长时间运行(如服务器程序),不手动释放内存会导致内存泄漏,最终耗尽系统资源。

写在最后 📝

malloccallocreallocfree 是动态内存管理的四大核心函数,掌握它们的关键在于:

  1. 牢记 malloc 使用五步走:申请→检查→使用→释放→置空。
  2. 区分 malloccalloc:是否需要初始化内存。
  3. 正确使用 realloc:用新指针接收返回值,失败时保留原内存。
  4. free 后必须置空指针:杜绝野指针和重复释放问题。

下一篇我们将深入讲解动态内存的常见错误经典笔试题分析,这些都是笔试面试的高频考点,千万不要错过!

相关推荐
yyy(十一月限定版)2 小时前
C语言——堆
c语言·开发语言·算法
澜莲花2 小时前
python图色之opencv基础
开发语言·图像处理·python·opencv
喜欢吃燃面2 小时前
算法竞赛中的数据结构:图
开发语言·数据结构·c++·学习·算法
哈市雪花2 小时前
记录一次cmake无法正确使用vcpkg的问题
开发语言·c++
Yue丶越2 小时前
【C语言】文件操作
服务器·c语言·开发语言
Trouvaille ~2 小时前
【C++篇】C++11新特性详解(三):高级特性与实用工具
开发语言·c++·stl·lambda·完美转发·包装器·可变参数模版
有趣灵魂2 小时前
Java-根据HTTP链接读取文件转换为base64
java·开发语言·http
AC赳赳老秦2 小时前
CSV大文件处理全流程:数据清洗、去重与格式标准化深度实践
大数据·开发语言·人工智能·python·算法·机器学习·deepseek
2501_930707782 小时前
如何使用C#代码将 Excel 文件转换为 SVG
开发语言·c#·excel