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

C语言自定义Vector引发疑似栈损坏、段错误问题排查

问题现象
  • 向函数传递参数时,函数接收到的参数值异常,尝试使用该错误值时会触发段错误(Segmentation fault)。
  • 程序尚未正式启动执行逻辑前,main函数中的argc、argv参数就已被损坏,GDB调试输出见下图:
    GDB output
相关代码

main.c

#include <stdio.h>
#include <stdlib.h>


#include "util/BasicTypes.h"
#include "util/IncludeOpengl.h"
#include "util/Vec2.h"
#include "util/Vec3.h"
#include "util/Vec4.h"
#include "util/Vector.h"


#if !(defined(__linux__) || defined(_WIN64))
    STATIC_ASSERT(false, unsupported_system)
#endif


i32 main(i32 argc, ch8** argv) {
    //u32 a[] = {1,2,3,4};
    //u32* b = a;
    u32 i;

    Vector v;
    init_Vector(&v, sizeof(u32));
    for (i = 0; i < 4; i++) {
        printf("%i\n", i);
        Vector_append(&v, &i);
    }

    for (i = 0; i < Vector_getSize(&v); i++) {
        printf("%i\n", Vector_get(&v, i));
    }

    destruct_Vector(&v);

    return 0;
}

vector.c

#ifndef VECTOR_C
#define VECTOR_C


#include "../util/Vector.h"


#include "../util/BasicTypes.h"
#include "../util/Assert.h"


void init_Vector(Vector* self, u32 elementSize) {
    self->elementSize = elementSize;
    self->elements = NULL;
    self->elementCount = 0;
}
void destruct_Vector(Vector* self) {
    if (self->elements) {
        heapFree(self->elements);
    }
}

void Vector_append(Vector* self, void* element) {
    if (self->elements) {
        self->elements = heapRealloc(self->elements, self->elementCount + 1);
        memcpy(((byte*)self->elements) + (self->elementCount) * self->elementSize, element, self->elementSize);
        self->elementCount += 1;
        return;
    }
    else {
        self->elements = heapAlloc((usize)self->elementSize);
        memcpy(self->elements, element, self->elementSize);
        self->elementCount += 1;
        return;
    }
}
void Vector_remove(Vector* self, u32 id) {
    if (id < self->elementCount) {
        void* temp = self->elements;
        self->elements = heapAlloc((self->elementCount - 1) * self->elementSize);
        memcpy(self->elements, temp, id * self->elementSize);
        memcpy(((byte*)self->elements) + id * self->elementSize, ((byte*)temp) + (id + 1) * self->elementSize, (self->elementCount - id - 1) * self->elementSize);
        self->elementCount -= 1;
    }
    else {
        ASSERT(false, "(Vector_remove) id is not in the list");
    }
}
void Vector_insert(Vector* self, void* element, u32 id) {
    if (!self->elements && !id) {
        self->elements = heapAlloc(self->elementSize);
        self->elementCount = 1;
    }
    else {
        if (id < self->elementCount + 1) {
            void* temp = self->elements;
            self->elements = heapAlloc((self->elementCount - 1) * self->elementSize);
            memcpy(self->elements, temp, id * self->elementSize);
            memcpy(((byte*)self->elements) + (id + 1) * self->elementSize, ((byte*)temp) + (id) * self->elementSize, (self->elementCount - id - 1) * self->elementSize);
            memcpy(((byte*)self->elements), ((byte*)temp), self->elementSize);
            self->elementCount += 1;
        }
        else {
            ASSERT(false, "(Vector_insert) id is too far");
        }
    }
}
void* Vector_get(Vector* self, u32 id) {
    return ((byte*)self->elements) + id * self->elementSize;
}
u32 Vector_getSize(Vector* self) {
    return self->elementCount;
}


#endif // VECTOR_C

vector.h

#ifndef VECTOR_H
#define VECTOR_H


#include "../util/BasicTypes.h"


typedef struct Vector {
    void* elements;
    u32 elementSize;
    u32 elementCount;
} Vector;


void init_Vector(Vector* self, u32 elementSize);
void destruct_Vector(Vector* self);

void Vector_append(Vector* self, void* element);
void Vector_remove(Vector* self, u32 id);
void Vector_insert(Vector* self, void* element, u32 id);
void* Vector_get(Vector* self, u32 id);
u32 Vector_getSize(Vector* self);


#endif // VECTOR_H

BasicTypes.h

#ifndef BASIC_TYPES_H
#define BASIC_TYPES_H


#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>


typedef int8_t         i8;
typedef int16_t        i16;
typedef int32_t        i32;
typedef int64_t        i64;

typedef uint8_t        u8;
typedef uint16_t       u16;
typedef uint32_t       u32;
typedef uint64_t       u64;

typedef i8             ch8;
typedef i16            ch16;
typedef i32            ch32;
typedef i64            ch64;

typedef u8             uch8;
typedef u16            uch16;
typedef u32            uch32;
typedef u64            uch64;

typedef float          f32;
typedef double         f64;
typedef long double    f128;

typedef u8             byte;
typedef size_t         usize;


#endif // BASIC_TYPES_H
根因分析

核心问题是动态内存分配时的大小计算错误,触发堆缓冲区溢出,直接破坏了栈上main函数的参数数据:

  1. 直接触发崩溃的bug在Vector_append函数:当vector已有元素执行扩容时,heapRealloc传入的新内存大小写的是self->elementCount + 1,没有乘以单个元素的字节数self->elementSize。
    以存储u32(单元素4字节)为例:第一次append走空指针分支,分配4字节存1个元素正常;第二次append时,实际需要2*4=8字节存储2个u32,但代码只申请了1+1=2字节,后续memcpy往这块不足的内存写入4字节数据时,就会越界覆盖堆相邻内存,甚至破坏栈帧里保存的argc、argv值,最终触发段错误。
  2. 其他隐藏bug:
    • Vector_insert函数插入元素需要的总大小应为(self->elementCount + 1) * self->elementSize,代码里写的是(self->elementCount - 1) * self->elementSize,分配的内存严重不足,同样会触发溢出。
    • Vector_remove、Vector_insert函数中重新分配内存后,没有释放temp保存的旧内存指针,会造成内存泄漏。
修复方案
  1. 修正Vector_append中的扩容大小计算,将realloc那行改为:
    self->elements = heapRealloc(self->elements, (self->elementCount + 1) * self->elementSize);
    
  2. 修正Vector_insert中的内存分配大小,将分配逻辑改为申请(self->elementCount + 1) * self->elementSize字节的内存。
  3. 在Vector_remove、Vector_insert完成内存拷贝后,调用heapFree(temp)释放旧内存,避免泄漏。
  4. 建议给vector增加容量字段,不要每次append都执行realloc,可按2倍速扩容减少内存分配开销,同时做边界检查避免越界访问。

内容的提问来源于stack exchange,提问作者brni-dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 07:24:24