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

含指针的自定义结构体使用memcpy为何引发堆缓冲区溢出?

问题根源:malloc的内存分配计算错误

你代码里的核心bug出在dynamic_array_create函数的malloc调用上:

dynamic_array.data = malloc(sizeof(DYNAMIC_STARTING_SIZE * element_size));

这里误用了sizeof运算符——你实际想分配的是DYNAMIC_STARTING_SIZE * element_size字节的内存,但加了sizeof之后,计算的是这个乘积值的类型大小(64位机器上size_t是8字节),而不是乘积本身的数值。

比如你的struct point大小是16字节(两个int各4字节+指针8字节),DYNAMIC_STARTING_SIZE * element_size的结果是16,但sizeof(16)等于sizeof(size_t)也就是8字节。这就导致你只分配了8字节内存,却要写入16字节的结构体数据,必然触发堆缓冲区溢出。

当结构体没有指针时,总大小是8字节(两个int),刚好和sizeof(size_t)一致,所以不会触发溢出,这就是为什么去掉指针字段问题消失的原因。

修复方案

把sizeof去掉,直接用乘积作为malloc的参数:

dynamic_array.data = malloc(DYNAMIC_STARTING_SIZE * element_size);

同时还要注意两个额外问题:

  • 必须检查malloc的返回值,如果分配失败会返回NULL,需要处理错误(比如断言或返回错误状态)
  • DYNAMIC_GROWTH_AMOUNT是浮点数1.5,用ceil计算扩容后的容量时,要引入<math.h>头文件,并且编译时链接数学库(比如gcc加-lm参数),否则会出现未定义行为。

修复后的dynamic_array_create函数:

#include <assert.h>
#include <math.h>

struct dynamic_array dynamic_array_create(size_t element_size) {
  struct dynamic_array dynamic_array;

  dynamic_array.data = malloc(DYNAMIC_STARTING_SIZE * element_size);
  assert(dynamic_array.data != NULL); // 简单错误检查,实际项目可替换为更健壮的处理逻辑

  dynamic_array.amount = 0;
  dynamic_array.capacity = DYNAMIC_STARTING_SIZE;
  dynamic_array.element_size = element_size;

  return dynamic_array;
}
额外修复:push函数的内存地址顺序问题

原dynamic_array_push里先计算目标地址再扩容的逻辑有潜在风险:如果realloc移动了内存块位置,之前计算的destination会指向旧的无效内存地址。正确的顺序应该是先判断扩容、调整内存,再计算目标地址:

void dynamic_array_push(struct dynamic_array* dynamic_array,
                        const void* element) {
  if (dynamic_array->amount >= dynamic_array->capacity) {
    dynamic_array->capacity =
        (size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
    void* new_data = realloc(dynamic_array->data,
                dynamic_array->capacity * dynamic_array->element_size);
    assert(new_data != NULL);
    dynamic_array->data = new_data;
  }

  unsigned char* destination =
      (unsigned char*)dynamic_array->data +
      dynamic_array->amount * dynamic_array->element_size;

  memcpy(destination, element, dynamic_array->element_size);
  dynamic_array->amount++;
}
验证修复

修复后,初始分配的内存大小就是1*16=16字节,刚好能容纳一个struct point实例,memcpy时不会再溢出,地址 sanitizer也不会报错。

内容的提问来源于stack exchange,提问作者Jagger Harris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:03:24