跳到主要内容
🔍

代码示例:1.3.9 动态内存管理与常见错误

全部示例均标注编译命令、适用环境与实测状态;完整验证记录见《03.5-代码编译运行验证记录》。

  • examples/ex1-dyn-array.c · malloc/calloc/realloc 正确用法与 realloc 安全模式

    /* ex1-dyn-array.c —— malloc/calloc/realloc 正确用法与 realloc 安全模式(1.3.9)
     * 适用环境:Linux / Windows(MinGW) 标准:C11
     * 编译:gcc ex1-dyn-array.c -o d1 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./d1(Windows: d1.exe)
     */
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void)
    {
        int n = 5;
        /* calloc 分配并清零;每次分配都检查 NULL */
        int *arr = calloc((size_t)n, sizeof(int));
        if (arr == NULL) {
            fprintf(stderr, "calloc failed\n");
            return 1;
        }
        printf("calloc 后 arr[0..4] 全部为 0: ");
        for (int i = 0; i < n; i++) printf("%d ", arr[i]);
        printf("\n");
     
        for (int i = 0; i < n; i++) arr[i] = (i + 1) * 10;
        long long sum = 0;
        for (int i = 0; i < n; i++) sum += arr[i];
        printf("sum = %lld\n", sum);
     
        /* realloc 安全模式:临时指针判空后再赋回原指针(避免失败时覆盖原指针造成泄漏) */
        int *tmp = realloc(arr, (size_t)(n + 3) * sizeof(int));
        if (tmp == NULL) {
            free(arr);
            fprintf(stderr, "realloc failed\n");
            return 1;
        }
        arr = tmp;
        n += 3;
        for (int i = 5; i < n; i++) arr[i] = i * 100;
        printf("扩容后: ");
        for (int i = 0; i < n; i++) printf("%d ", arr[i]);
        printf("\n");
     
        free(arr);
        return 0;
    }
     
  • examples/ex2-struct-pool.c · 动态结构体数组 + create/destroy 所有权封装

    /* ex2-struct-pool.c —— 动态结构体数组 + create/destroy 所有权封装(1.3.9)
     * 适用环境:Linux / Windows(MinGW) 标准:C11
     * 编译:gcc ex2-struct-pool.c -o d2 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./d2(Windows: d2.exe)
     * 说明:pool_create 失败路径先释放已分配的部分再返回 NULL——"分配多少清理多少"
     */
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct { int id; char name[32]; } item_t;
     
    typedef struct { item_t *items; int count; } pool_t;
     
    static pool_t *pool_create(int n)
    {
        pool_t *p = malloc(sizeof(pool_t));
        if (p == NULL) return NULL;
        p->items = calloc((size_t)n, sizeof(item_t));
        if (p->items == NULL) {
            free(p);
            return NULL;
        }
        p->count = n;
        return p;
    }
     
    static void pool_destroy(pool_t *p)
    {
        if (p == NULL) return;
        free(p->items);
        free(p);
    }
     
    int main(void)
    {
        pool_t *pool = pool_create(3);
        if (pool == NULL) {
            fprintf(stderr, "create failed\n");
            return 1;
        }
        for (int i = 0; i < pool->count; i++) {
            pool->items[i].id = 100 + i;
            snprintf(pool->items[i].name, sizeof(pool->items[i].name), "item-%d", i);
        }
        for (int i = 0; i < pool->count; i++)
            printf("items[%d] = { id=%d, name=%s }\n", i, pool->items[i].id, pool->items[i].name);
        pool_destroy(pool);
        return 0;
    }
     
  • examples/ex3-memory-pitfalls.txt · 内存四类问题与检测思路(conceptual:静态讲解,不运行危险代码)

    # 1.3.9 ex3(conceptual 讲解文本,不运行):内存四类问题与检测思路
    #
    # 本文件是"概念讲解型"示例(example_kind=conceptual):只讲解与引工具文档,
    # 绝不实际运行 double free / use-after-free 等未定义行为代码(可能破坏进程状态)。
    #
    # 一、四类内存问题(全部属于"未定义行为"或资源错误,标准不保证结果)
    # 1. 内存泄漏(leak):分配后指针丢失/无人 free——不是 UB,但长期运行内存耗尽。
    #    Valgrind 报告:X bytes in N blocks are definitely lost("确定泄漏")
    # 2. 重复释放(double free):同一指针 free 两次——UB,可能破坏堆结构导致崩溃。
    #    Valgrind 报告:Invalid free() / delete
    # 3. 释放后使用(use-after-free):free 后继续读/写该指针——UB,可能读到被复用数据。
    #    Valgrind 报告:Invalid read/write of size N(堆块已释放)
    # 4. 越界写(buffer overflow):写越过分配边界——UB,破坏相邻堆块元数据。
    #    Valgrind 报告:Invalid write of size N
    #
    # 二、检测思路(工具文档核对;本机无 Linux,未执行)
    # Linux + Valgrind(Memcheck):
    #   gcc -g -O0 prog.c -o prog
    #   valgrind --leak-check=full --track-origins=yes ./prog
    #   读报告:先看 Invalid read/write(越界/UAF,最危险)→ 再看 definitely lost(泄漏)
    # 备选/跨平台:AddressSanitizer(-fsanitize=address,编译期插桩,5.6 调试章节展开)
    # 嵌入式:难以跑 Valgrind——靠 代码审查(create/destroy 配对)、分配/释放计数统计、
    #           任务栈水位监控;中断上下文禁止 malloc。
    #
    # 三、工程防线(本章纪律)
    # - 每次分配判 NULL;realloc 用临时指针判空后赋回(ex1 安全模式);
    # - free 后置 NULL(防"重复 free"与"悬空指针误用"的直接触发);
    # - 所有权约定:谁分配谁释放,create/destroy 成对(ex2);
    # - 分配大小用 n * sizeof(T) 并注意乘法溢出(SIZE_MAX 边界,conceptual 说明)。
    #
    # 验证依据(documentation_checked):Valgrind Memcheck 官方手册(错误类型与报告字段)、
    # C11 7.22.3 内存管理函数语义;未在本机执行 Valgrind(本机无 Linux)。