文章目录

C 语言内存管理:malloc() 与 free() 的正确使用

发布于 2026-04-03 12:22:14 · 浏览 6 次 · 评论 0 条

C 语言内存管理:malloc() 与 free() 的正确使用

C 语言不提供自动内存回收机制,程序员必须手动申请和释放动态内存。malloc()free() 是最基础、最关键的两个函数。用错它们会导致程序崩溃、内存泄漏或安全漏洞。


理解动态内存的基本规则

动态内存是指程序运行时从堆(heap)中临时借用的一块空间。它不会像局部变量那样在函数结束时自动消失,也不会像全局变量那样一直存在。你必须自己决定何时申请、何时归还。

  • 申请内存用 malloc():告诉系统“我需要 N 字节”,系统返回这块内存的起始地址。
  • 释放内存用 free():告诉系统“我不再需要这块内存了,请收回”。

这两个操作必须严格配对:每调用一次 malloc(),就必须且只能调用一次 free()


正确使用 malloc() 的步骤

  1. 包含头文件
    在代码开头写上 #include <stdlib.h>。这是 malloc()free() 的声明所在。

  2. 计算所需字节数
    使用 sizeof 运算符获取单个元素的大小,再乘以元素个数。例如:

    int *arr = malloc(10 * sizeof(int));

    这表示申请能存放 10 个 int 类型数据的空间。

  3. 检查返回值是否为 NULL
    如果系统内存不足,malloc() 会返回 NULL必须检查这个值,否则后续操作可能导致程序崩溃

    int *ptr = malloc(100 * sizeof(int));
    if (ptr == NULL) {
        // 处理错误,比如退出程序或提示用户
        return -1;
    }
  4. 不要对未初始化的内存做假设
    malloc() 返回的内存内容是未定义的(可能是任意垃圾值)。如果需要清零,应改用 calloc(),或手动用 memset() 初始化。


正确使用 free() 的步骤

  1. 只释放通过 malloc/calloc/realloc 申请的内存
    不能对栈变量、全局变量或字符串字面量调用 free()。例如以下代码是严重错误:

    int x = 5;
    free(&x); // 错误!x 是栈变量
    char *s = "hello";
    free(s);  // 错误!"hello" 是常量区字符串
  2. 释放后立即将指针设为 NULL
    释放内存后,原指针仍然保存着旧地址(称为“悬空指针”)。再次使用它(读或写)会导致未定义行为。释放后应立即将指针赋值为 NULL

    free(ptr);
    ptr = NULL; // 防止后续误用
  3. 不要重复释放同一块内存
    对同一个非 NULL 指针多次调用 free() 会导致程序崩溃。例如:

    free(ptr);
    free(ptr); // 危险!第二次释放已释放的内存

    将指针设为 NULL 可避免此问题,因为 free(NULL) 是安全的(什么也不做)。


常见错误模式与规避方法

错误类型 错误代码示例 正确做法
内存泄漏 c<br>void func() {<br> int *p = malloc(100);<br> // 忘记 free(p)<br>} | 函数结束前确保 free(p) 被执行
重复释放 c<br>free(p);<br>free(p); | 释放后立即 p = NULL;
使用已释放内存 c<br>free(p);<br>*p = 10; // 危险 释放后不再访问该指针
申请 0 字节 c<br>malloc(0); // 行为未定义 避免传入 0,或显式处理

实战:安全封装动态数组

为了减少出错,可以将内存管理逻辑封装成简单函数:

#include <stdio.h>
#include <stdlib.h>

// 安全申请整数数组
int* create_int_array(size_t size) {
    if (size == 0) return NULL;
    int *arr = malloc(size * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "内存申请失败\n");
        return NULL;
    }
    return arr;
}

// 安全释放并置空
void destroy_int_array(int **arr) {
    if (arr != NULL && *arr != NULL) {
        free(*arr);
        *arr = NULL;
    }
}

// 使用示例
int main() {
    int *data = create_int_array(50);
    if (data == NULL) return -1;

    data[0] = 42; // 正常使用

    destroy_int_array(&data); // 自动置 NULL
    // 此时 data == NULL,无法误用

    return 0;
}

关键点

  • 申请函数负责检查和初始化。
  • 释放函数接受指针的地址int **),从而能修改原指针的值为 NULL
  • 调用者无需记住 free 后手动置空。

特殊注意事项

  • realloc() 的陷阱realloc(ptr, new_size) 可能移动内存块。如果成功,原 ptr 失效;如果失败,原内存仍有效。不要直接赋值给原指针

    // 错误
    ptr = realloc(ptr, new_size); // 若失败,ptr 变 NULL,原内存丢失!
    
    // 正确
    void *temp = realloc(ptr, new_size);
    if (temp != NULL) {
        ptr = temp;
    } else {
        // 处理错误,原 ptr 仍可用
    }
  • 跨函数传递指针:当指针作为参数传入函数时,函数内部释放内存不会影响调用者的指针值。如需同步置空,必须传入指针的地址(如前述 destroy_int_array 所示)。

  • 多线程环境malloc()free() 本身是线程安全的,但多个线程同时操作同一块内存仍需加锁保护,防止竞争条件。


最终检查清单

每次使用动态内存后,问自己四个问题:

  1. 是否检查了 malloc() 返回值?
  2. 是否只对堆内存调用了 free()
  3. 是否确保每块内存只被 free() 一次?
  4. 释放后是否将指针设为 NULL

只要全部回答“是”,就能避免绝大多数内存管理错误。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文