C语言实现动态数组时free释放内存触发段错误问题排查
动态数组free函数触发段错误根因修复
问题现象
实现动态数组工具函数时,采用自定义Vector结构体存储数组数据与元数据,设计为向用户返回数组操作入口指针,元数据通过配套接口访问。除堆内存释放逻辑外其余功能可正常运行,执行free_vector时无明确提示触发段错误,已通过打印确认传入free_vector的vec指针地址与返回的数组入口地址一致,但free调用始终失败。
原问题代码如下:
typedef struct { size_t allocated_length; size_t active_length; size_t num_bytes; char *vector; } Vector; void *init_vector(size_t num_indices, size_t num_bytes) { // Allocate memory for Vector struct Vector *vec = malloc(sizeof(*vec)); vec->active_length = 0; vec->num_bytes = num_bytes; // Allocate heap memory for vector void *ptr = malloc(num_bytes * num_indices); if (ptr == NULL) { printf("WARNING: Unable to allocate memory, exiting!\n"); return &vec->vector; } vec->allocated_length = num_indices; vec->vector = ptr; return &vec->vector; } // -------------------------------------------------------------------------------- int push_vector(void *vec, void *elements, size_t num_indices) { Vector *a = get_vector_data(vec); if(a->active_length + num_indices > a->allocated_length) { printf("TRUE\n"); size_t size = (a->allocated_length + num_indices) * 2; void *ptr = realloc(a->vector, size * a->num_bytes); if (ptr == NULL) { printf("WARNING: Unable to allocate memory, exiting!\n"); return 0; } a->vector = ptr; a->allocated_length = size; } memcpy((char *)vec + a->active_length * a->num_bytes, elements, num_indices * a->num_bytes); a->active_length += num_indices; return 1; } // -------------------------------------------------------------------------------- Vector *get_vector_data(void *vec) { // - The Vector struct has three size_t variables that proceed the vector // variable. These variables consume 24 bytes of daya. THe code below // points backwards in memory by 24 bytes to the beginning of the Struct. char *a = (char *)vec - 24; return (Vector *)a; } // -------------------------------------------------------------------------------- void free_vector(void *vec) { // Free all Vector struct elements Vector *a = get_vector_data(vec); // - This print statement shows that the variable is pointing to the // correct data. printf("%d\n" ((int *)vec)[2]); // The function fails on the next line and I do not know why free(a->vector); a->vector = NULL; a->allocated_length = 0; a->active_length = 0; a->num_bytes = 0; } int main() { int *a = init_vector(3, sizeof(int)); int b[3] = {1,2,3}; push_vector(a, b, 3); // 执行到此处崩溃 free_vector(a); }
根因定位
崩溃核心问题有3个,按影响优先级排序:
- 硬编码结构体成员偏移是致命错误:
get_vector_data中手动减24字节回溯结构体首地址的写法完全不具备可靠性:- 不同系统位宽下
size_t和指针长度不一致:32位环境下size_t占4字节,3个元数据字段仅占12字节,vector成员偏移为12,减24会直接定位到结构体之外的野内存,读取到的a->vector是随机值,free随机地址必然触发段错误。 - 就算在64位环境下,结构体可能存在编译器自动插入的对齐填充字节,手算偏移无法保证和实际内存布局一致,只要编译选项、结构体成员顺序变动,偏移值就会失效。
- 写的打印验证逻辑本身有问题:传入的vec是数组元素内存的首地址,强转为
int*读取偏移2位置的内容,读到的是数组的第三个元素(也就是push进去的3),根本不是结构体元数据,属于误判“地址正确”。
- 不同系统位宽下
- 内存分配失败分支返回野指针:
init_vector中如果给数组元素申请内存失败,没有给vec->vector赋值就直接返回其地址,后续操作这个野指针必然崩溃;同时原代码没有判断Vector结构体本身的malloc返回值,极端内存不足场景下也会触发空指针访问。 - 内存释放逻辑不完整+语法错误:
free_vector中只释放了数组元素的内存,没有释放Vector结构体本身的内存,存在固定内存泄漏;另外printf语句漏了参数分隔逗号,本身就存在语法错误,无法正常编译运行。
修复方案
- 引入标准库
stddef.h提供的offsetof宏获取结构体成员的真实偏移,禁止硬编码偏移值。 - 补全所有内存分配分支的失败处理,初始化所有结构体字段,申请失败时主动释放已拿到的内存避免泄漏。
- 释放内存时先释放数组元素内存,再释放Vector结构体本身,避免内存泄漏。
- 修正语法错误,补充空指针判断提升鲁棒性。
修复后的可运行代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> typedef struct { size_t allocated_length; size_t active_length; size_t num_bytes; char *vector; } Vector; Vector *get_vector_data(void *vec) { // 用标准宏获取真实偏移,不硬编码 return (Vector*)((char*)vec - offsetof(Vector, vector)); } void *init_vector(size_t num_indices, size_t num_bytes) { Vector *vec = malloc(sizeof(*vec)); if (vec == NULL) { printf("WARNING: Unable to allocate struct memory, exiting!\n"); return NULL; } vec->active_length = 0; vec->num_bytes = num_bytes; vec->allocated_length = num_indices; vec->vector = malloc(num_bytes * num_indices); if (vec->vector == NULL) { printf("WARNING: Unable to allocate array memory, exiting!\n"); free(vec); // 申请失败先释放已经申请的结构体内存 return NULL; } return vec->vector; } int push_vector(void *vec, void *elements, size_t num_indices) { if (vec == NULL || elements == NULL) return 0; Vector *a = get_vector_data(vec); if(a->active_length + num_indices > a->allocated_length) { size_t new_size = (a->allocated_length + num_indices) * 2; void *new_ptr = realloc(a->vector, new_size * a->num_bytes); if (new_ptr == NULL) { printf("WARNING: Unable to reallocate memory, exiting!\n"); return 0; } a->vector = new_ptr; a->allocated_length = new_size; } memcpy((char *)vec + a->active_length * a->num_bytes, elements, num_indices * a->num_bytes); a->active_length += num_indices; return 1; } void free_vector(void *vec) { if (vec == NULL) return; Vector *a = get_vector_data(vec); free(a->vector); free(a); // 释放结构体本身的内存 } int main() { int *a = init_vector(3, sizeof(int)); if (a == NULL) return 1; int b[3] = {1, 2, 3}; push_vector(a, b, 3); // 打印验证 for (int i=0; i<3; i++) { printf("%d ", a[i]); } printf("\n"); free_vector(a); return 0; }
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

