You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:24:23