内存管理

翻译整理自 cppreference - 内存分配。

1. 内存分区

C 程序运行时内存大致分:

区 用途
代码段 (text) 程序指令
只读数据 (rodata) 字符串字面量、const
数据段 (data) 已初始化全局/静态
BSS 未初始化或零初始化全局/静态
栈 (stack) 局部变量、函数调用
堆 (heap) 动态分配

2. 栈内存

void f(void) {
    int x = 10;              // 栈上
    int arr[100];             // 栈数组,大小固定
}

特点:自动分配释放,速度快,但空间有限(默认 Linux 8MB)。

3. 静态存储期

int global;                 // 全局,零初始化
static int file_local;       // 文件内可见
void f(void) {
    static int count = 0;    // 仅初始化一次,跨调用保留
    count++;
}

4. 堆内存 malloc / calloc / realloc / free

#include <stdlib.h>

int *p = malloc(4 * sizeof(int));   // 不初始化
if (!p) { perror("malloc"); exit(1); }

int *q = calloc(4, sizeof(int));   // 清零
q = realloc(q, 8 * sizeof(int));    // 调整大小
free(p); free(q);
p = NULL; q = NULL;

malloc 失败检查

int *buf = malloc(huge);
if (!buf) {
    fprintf(stderr, "out of memory\n");
    exit(1);
}

5. 内存泄漏

void leak(void) {
    int *p = malloc(100);
    // 忘记 free → 泄漏
}

工具检测:valgrind --leak-check=full ./a.out、AddressSanitizer:

gcc -fsanitize=address -g prog.c -o prog
./prog

6. 二次释放与悬空指针

int *p = malloc(4);
free(p);
free(p);           // UB! 二次释放
*p = 1;            // UB! use after free

修复:释放后置 NULL:

free(p); p = NULL;

7. 内存越界

int arr[10];
arr[10] = 0;        // 越界写 UB
arr[-1] = 0;        // 负索引 UB

8. 动态字符串示例

char *str_dup(const char *src) {
    size_t n = strlen(src) + 1;
    char *dst = malloc(n);
    if (dst) memcpy(dst, src, n);
    return dst;
}

char *s = str_dup("hello");
free(s);

9. 二维动态数组

int **m = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++)
    m[i] = malloc(cols * sizeof(int));

// 使用
m[2][3] = 99;

// 释放
for (int i = 0; i < rows; i++) free(m[i]);
free(m);

一次性连续分配更高效:

int *flat = malloc(rows * cols * sizeof(int));
int (*m)[cols] = (int (*)[cols])flat;   // 变长数组指针(C99)
m[2][3] = 99;
free(flat);

10. 自适应字符串缓冲

typedef struct {
    char  *data;
    size_t len, cap;
} StrBuf;

void sb_init(StrBuf *s) {
    s->cap = 16;
    s->len = 0;
    s->data = malloc(s->cap);
}

void sb_push(StrBuf *s, char c) {
    if (s->len == s->cap) {
        s->cap *= 2;
        s->data  = realloc(s->data, s->cap);
    }
    s->data[s->len++] = c;
}

void sb_free(StrBuf *s) { free(s->data); s->data = NULL; }

11. 常见错误

  • 数组越界
  • free 非堆内存
  • 内存泄漏
  • 使用未初始化的内存
  • 二次释放
  • 返回局部变量地址
  • 字符串未 \0 结尾

小结

C 不提供自动垃圾回收,每一处 malloc 必须对应 free。下一章学习输入输出。