19 · 内存管理 ★C 的第二大关卡

2026.09.27 · 🔴🔴 最难

Java 有 GC(垃圾回收);C 没有。你申请的内存,必须你自己还。

内存分区(脑中要有图)

高地址 ┌──────────────┐
       │   栈 Stack    │ 局部变量、函数调用 —— 自动分配/回收
       ├──────────────┤
       │  堆 Heap      │ malloc 分配 —— 手动 malloc / free
       ├──────────────┤
       │ 全局/静态区    │ 全局变量、static —— 程序全程存活,自动清零
       ├──────────────┤
       │ 常量区        │ "字符串字面量" —— 只读
低地址 └──────────────┘

四个函数

#include <stdlib.h>

int *p = (int *)malloc(5 * sizeof(int));   // 申请 5 个 int,内容未初始化
int *q = (int *)calloc(5, sizeof(int));     // 申请并**全部清零**
p = (int *)realloc(p, 10 * sizeof(int));    // 扩容到 10 个 int

free(p);        // ✅ 用完必须释放
p = NULL;       // ✅ 释放后置 NULL,防止悬空指针

三条铁律(背下来)

  1. malloc 后必须判空(内存可能申请失败) c if (p == NULL) { /* 处理内存不足 */ }
  2. 谁分配谁释放,malloc 与 free 一一配对(否则内存泄漏)
  3. free 之后立刻把指针置 NULL(否则悬空指针,再解引用就是未定义行为)

典型错误

错误 后果
忘记 free 内存泄漏,长时间运行的程序越吃越多
free 两次 double free,崩溃
free 后继续用 悬空指针,未定义行为
访问越界 破坏相邻内存,神秘崩溃
返回局部数组的地址 栈已销毁,读到垃圾

新手必备工具

  • 每个 malloc 旁边写注释提醒自己 free。
  • 大项目用 Valgrind(Linux)/ ASan(gcc -fsanitize=address)检测泄漏。

⚡ Java 对比

// C:完全手动
int *arr = malloc(n * sizeof(int));
/* ... 用完 ... */
free(arr);
// Java:交给 GC
int[] arr = new int[n];
// 不用管,GC 会回收

学 C 最值钱的收获就是:你终于知道 Java 里那些「不用管的东西」背后发生了什么。

关联

09-指针 · 08-数组 · 12-结构体 · 20-未定义行为与安全函数