C语言动态内存管理完全指南
1. 内存分配函数
C语言提供四个基本的动态内存管理函数,声明在 stdlib.h 中:
malloc - 分配原始内存
void *malloc(size_t size);
/* 成功返回指针,失败返回 NULL */
int *arr = (int *)malloc(10 * sizeof(int));
if (arr == NULL) { /* 必须检查 */
perror("malloc");
exit(1);
}
calloc - 分配并初始化为零
void *calloc(size_t nmemb, size_t size);
/* 分配 nmemb 个元素,每个 size 字节,全部初始化为 0 */
int *arr = (int *)calloc(10, sizeof(int));
/* 等价于 malloc + memset(arr, 0, 10*sizeof(int)) */
realloc - 调整已分配内存大小
void *realloc(void *ptr, size_t size);
/* 扩大数组容量 */
int *new_arr = (int *)realloc(arr, 20 * sizeof(int));
if (new_arr == NULL) {
free(arr); /* 释放原内存 */
exit(1);
}
arr = new_arr;
realloc 可能返回新地址,必须使用返回值更新指针。
free - 释放内存
void free(void *ptr);
/* 释放后应将指针置为 NULL,避免悬空指针 */
free(arr);
arr = NULL;
2. 常见内存错误
- 未检查返回值:malloc/calloc/realloc 可能返回 NULL
- 内存泄漏:分配后未释放,导致内存逐渐耗尽
- 重复释放:free 同一块内存两次,未定义行为
- 使用已释放内存:悬空指针解引用
- 越界访问:写入超出分配范围的数据
/* 典型错误示例 */
/* 错误1: 未检查返回值 */
int *p = malloc(100);
strcpy(p, "hello"); /* p 可能为 NULL */
/* 错误2: 内存泄漏 */
void leak(void) {
int *p = malloc(1024);
/* 忘记 free(p) */
}
/* 错误3: 重复释放 */
free(p);
free(p); /* 崩溃或未定义行为 */
3. 内存池实现
频繁调用 malloc/free 会产生内存碎片,内存池通过预分配大块内存,按需分配,提高性能。
/* 简单内存池实现 */
#include
#include
typedef struct MemPool {
char *buffer;
size_t capacity;
size_t offset;
} MemPool;
MemPool *pool_create(size_t size) {
MemPool *pool = malloc(sizeof(MemPool));
pool->buffer = malloc(size);
pool->capacity = size;
pool->offset = 0;
return pool;
}
void *pool_alloc(MemPool *pool, size_t size) {
if (pool->offset + size > pool->capacity) return NULL;
void *ptr = pool->buffer + pool->offset;
pool->offset += size;
return ptr;
}
void pool_destroy(MemPool *pool) {
free(pool->buffer);
free(pool);
}
4. 内存调试工具
- Valgrind:检测内存泄漏、越界访问、使用未初始化内存
- AddressSanitizer (ASan):编译时添加 -fsanitize=address,快速定位内存问题
- mtrace:glibc 内置的内存分配跟踪工具
/* 使用 Valgrind 检测内存泄漏 */
/* 编译:gcc -g -o prog prog.c */
/* 运行:valgrind --leak-check=full ./prog */
最佳实践:1) 始终检查 malloc 返回值;2) 遵循谁分配谁释放原则;3) 释放后立即置 NULL;4) 频繁分配场景考虑内存池。