名称: c-内存管理 用户可调用: false 描述: 在C程序中使用malloc/free、指针以及避免常见内存安全陷阱时使用。 允许工具:
- 读取
- 写入
- 编辑
- 搜索
- 全局
- Bash
C内存管理
掌握C中的手动内存管理,包括正确分配、释放、指针处理,以及避免内存泄露和损坏的技术。
概述
C需要通过显式分配和释放来进行手动内存管理。理解指针、堆和正确内存处理对于编写安全高效的C程序至关重要。
安装和设置
编译器和工具
# 安装GCC编译器
# macOS
xcode-select --install
# Linux (Ubuntu/Debian)
sudo apt-get install build-essential
# 检查安装
gcc --version
# 内存调试工具
# 安装Valgrind (Linux)
sudo apt-get install valgrind
# 安装地址消毒器 (内置在GCC/Clang中)
gcc -fsanitize=address -g program.c -o program
编译标志
# 基本编译
gcc program.c -o program
# 带警告和调试
gcc -Wall -Wextra -g program.c -o program
# 带地址消毒器
gcc -fsanitize=address -g program.c -o program
# 带优化
gcc -O2 -Wall program.c -o program
核心模式
1. 动态内存分配
// malloc - 分配内存
#include <stdlib.h>
#include <string.h>
int* allocate_array(size_t size) {
int* arr = malloc(size * sizeof(int));
if (arr == NULL) {
return NULL; // 分配失败
}
return arr;
}
// calloc - 分配并零初始化
int* allocate_zeroed_array(size_t size) {
int* arr = calloc(size, sizeof(int));
if (arr == NULL) {
return NULL;
}
return arr;
}
// realloc - 调整分配大小
int* resize_array(int* arr, size_t old_size, size_t new_size) {
int* new_arr = realloc(arr, new_size * sizeof(int));
if (new_arr == NULL && new_size > 0) {
// 重新分配失败,原始数组仍然有效
return NULL;
}
return new_arr;
}
// free - 释放内存
void cleanup_array(int** arr) {
if (arr != NULL && *arr != NULL) {
free(*arr);
*arr = NULL; // 防止悬空指针
}
}
2. 指针基础
// 指针声明和使用
void pointer_basics() {
int value = 42;
int* ptr = &value; // ptr指向value
printf("值: %d
", value);
printf("地址: %p
", (void*)&value);
printf("指针: %p
", (void*)ptr);
printf("解引用: %d
", *ptr);
*ptr = 100; // 通过指针修改
printf("新值: %d
", value);
}
// 空指针
void null_pointer_check(int* ptr) {
if (ptr == NULL) {
printf("空指针
");
return;
}
printf("有效指针: %d
", *ptr);
}
// 指针算术
void pointer_arithmetic() {
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr;
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i)); // 同ptr[i]
}
printf("
");
}
3. 动态字符串
#include <string.h>
// 创建字符串副本
char* string_duplicate(const char* str) {
if (str == NULL) {
return NULL;
}
size_t len = strlen(str);
char* copy = malloc(len + 1); // +1用于空终止符
if (copy != NULL) {
strcpy(copy, str);
}
return copy;
}
// 字符串连接
char* string_concat(const char* s1, const char* s2) {
if (s1 == NULL || s2 == NULL) {
return NULL;
}
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
char* result = malloc(len1 + len2 + 1);
if (result == NULL) {
return NULL;
}
strcpy(result, s1);
strcat(result, s2);
return result;
}
// 安全字符串函数
char* safe_string_copy(const char* src, size_t max_len) {
if (src == NULL) {
return NULL;
}
size_t len = strnlen(src, max_len);
char* dest = malloc(len + 1);
if (dest != NULL) {
memcpy(dest, src, len);
dest[len] = '\0';
}
return dest;
}
4. 结构和内存
// 带动态成员的结构
typedef struct {
char* name;
int* scores;
size_t num_scores;
} Student;
Student* create_student(const char* name, size_t num_scores) {
Student* student = malloc(sizeof(Student));
if (student == NULL) {
return NULL;
}
student->name = string_duplicate(name);
if (student->name == NULL) {
free(student);
return NULL;
}
student->scores = malloc(num_scores * sizeof(int));
if (student->scores == NULL) {
free(student->name);
free(student);
return NULL;
}
student->num_scores = num_scores;
memset(student->scores, 0, num_scores * sizeof(int));
return student;
}
void destroy_student(Student** student) {
if (student == NULL || *student == NULL) {
return;
}
free((*student)->name);
free((*student)->scores);
free(*student);
*student = NULL;
}
5. 内存池
// 简单内存池
typedef struct {
void* pool;
size_t block_size;
size_t num_blocks;
size_t next_free;
} MemoryPool;
MemoryPool* create_pool(size_t block_size, size_t num_blocks) {
MemoryPool* pool = malloc(sizeof(MemoryPool));
if (pool == NULL) {
return NULL;
}
pool->pool = malloc(block_size * num_blocks);
if (pool->pool == NULL) {
free(pool);
return NULL;
}
pool->block_size = block_size;
pool->num_blocks = num_blocks;
pool->next_free = 0;
return pool;
}
void* pool_allocate(MemoryPool* pool) {
if (pool == NULL || pool->next_free >= pool->num_blocks) {
return NULL;
}
void* block = (char*)pool->pool + (pool->next_free * pool->block_size);
pool->next_free++;
return block;
}
void destroy_pool(MemoryPool** pool) {
if (pool == NULL || *pool == NULL) {
return;
}
free((*pool)->pool);
free(*pool);
*pool = NULL;
}
6. 引用计数
// 引用计数字符串
typedef struct {
char* data;
size_t ref_count;
} RefString;
RefString* refstring_create(const char* str) {
RefString* rs = malloc(sizeof(RefString));
if (rs == NULL) {
return NULL;
}
rs->data = string_duplicate(str);
if (rs->data == NULL) {
free(rs);
return NULL;
}
rs->ref_count = 1;
return rs;
}
RefString* refstring_retain(RefString* rs) {
if (rs != NULL) {
rs->ref_count++;
}
return rs;
}
void refstring_release(RefString** rs) {
if (rs == NULL || *rs == NULL) {
return;
}
(*rs)->ref_count--;
if ((*rs)->ref_count == 0) {
free((*rs)->data);
free(*rs);
}
*rs = NULL;
}
7. 内存调试
// 用于malloc的调试包装器
#ifdef DEBUG_MEMORY
typedef struct {
void* ptr;
size_t size;
const char* file;
int line;
} AllocationInfo;
static AllocationInfo allocations[1000];
static size_t num_allocations = 0;
void* debug_malloc(size_t size, const char* file, int line) {
void* ptr = malloc(size);
if (ptr != NULL && num_allocations < 1000) {
allocations[num_allocations].ptr = ptr;
allocations[num_allocations].size = size;
allocations[num_allocations].file = file;
allocations[num_allocations].line = line;
num_allocations++;
}
return ptr;
}
void debug_free(void* ptr) {
for (size_t i = 0; i < num_allocations; i++) {
if (allocations[i].ptr == ptr) {
allocations[i] = allocations[num_allocations - 1];
num_allocations--;
break;
}
}
free(ptr);
}
void print_leaks() {
printf("内存泄露: %zu
", num_allocations);
for (size_t i = 0; i < num_allocations; i++) {
printf(" %p (%zu 字节) 在 %s:%d
",
allocations[i].ptr,
allocations[i].size,
allocations[i].file,
allocations[i].line);
}
}
#define malloc(size) debug_malloc(size, __FILE__, __LINE__)
#define free(ptr) debug_free(ptr)
#endif
8. 栈 vs 堆
// 栈分配
void stack_example() {
int local_var = 42; // 栈
char buffer[100]; // 栈
// 函数返回时自动释放
}
// 堆分配
void heap_example() {
int* dynamic = malloc(sizeof(int)); // 堆
if (dynamic != NULL) {
*dynamic = 42;
free(dynamic); // 必须手动释放
}
}
// 混合分配
typedef struct {
int id; // 栈(结构的一部分)
char* name; // 堆(指向堆的指针)
} Record;
Record* create_record(int id, const char* name) {
Record* rec = malloc(sizeof(Record)); // 堆
if (rec == NULL) {
return NULL;
}
rec->id = id; // 栈值
rec->name = string_duplicate(name); // 堆
if (rec->name == NULL) {
free(rec);
return NULL;
}
return rec;
}
9. 双重释放预防
// 安全释放宏
#define SAFE_FREE(ptr) do { \
if (ptr != NULL) { \
free(ptr); \
ptr = NULL; \
} \
} while(0)
// 用法
void safe_cleanup() {
int* arr = malloc(10 * sizeof(int));
// ... 使用 arr ...
SAFE_FREE(arr);
// arr现在为NULL,可以安全再次调用
SAFE_FREE(arr); // 无操作,安全
}
// 引用清除
void clear_reference(void** ref) {
if (ref != NULL && *ref != NULL) {
free(*ref);
*ref = NULL;
}
}
10. 内存对齐
#include <stdalign.h>
// 对齐分配
void* aligned_malloc(size_t size, size_t alignment) {
void* ptr = NULL;
#ifdef _WIN32
ptr = _aligned_malloc(size, alignment);
#else
if (posix_memalign(&ptr, alignment, size) != 0) {
return NULL;
}
#endif
return ptr;
}
void aligned_free(void* ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
// 结构对齐
typedef struct {
alignas(16) double values[4]; // 16字节对齐
} AlignedData;
最佳实践
- 始终检查malloc返回值 - 处理分配失败
- 释放所有分配的内存 - 防止内存泄露
- 释放后设置指针为NULL - 避免悬空指针
- 使用sizeof与类型 - 确保正确分配大小
- 初始化分配的内存 - 使用calloc或memset
- 匹配malloc/free调用 - 每个分配都需要释放
- 使用valgrind进行测试 - 检测内存错误
- 避免手动指针算术 - 尽可能使用数组索引
- 处理realloc失败 - 保持原始指针有效
- 文档化所有权 - 明确谁释放内存
常见陷阱
- 内存泄露 - 忘记释放分配的内存
- 双重释放 - 释放同一指针两次
- 释放后使用 - 访问已释放内存
- 缓冲区溢出 - 写入超出分配边界
- 悬空指针 - 使用释放后的指针
- 空指针解引用 - 未检查NULL
- sizeof错误 - 使用错误的大小计算
- 栈溢出 - 大栈分配
- 未初始化内存 - 读取未初始化数据
- 内存碎片化 - 不良分配模式
何时使用
- 需要手动控制的系统编程
- 资源有限的嵌入式系统
- 性能关键应用
- 操作系统开发
- 设备驱动和内核模块
- 实时系统
- 遗留代码库维护
- 与硬件接口
- 内存受限环境
- 低级库开发