代码示例: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)。