我正在编写一个程序,需要C89中的多种类型的动态数组。我想做的是创建我自己的支持自定义结构数据的动态数组的实现。
但是,在使用动态数组实现时,我遇到堆缓冲区溢出,但我无法弄清楚原因。我似乎没有选角问题。我已正确分配动态数组并调整其大小。我使用了合适的尺寸。我应该能够毫无问题地将结构添加到数组中。
这是包含动态数组和点结构的主 C 文件。堆缓冲区溢出发生在
dynamic_array_push
。注意:出于测试目的,我创建了一个 point_a
变量,因此在此实例中没有创建动态数组的实际目的。
struct point {
int x;
int y;
const char* id;
};
int main() {
struct dynamic_array points = dynamic_array_create(sizeof(struct point));
struct point point_a = {1, 1, "point_a"};
size_t i;
dynamic_array_push(&points, (void*)&point_a); /* Heap buffer overflow here */
return 0;
}
这是动态数组头文件,其中包含创建和推送动态数组的最小功能(不包括释放内存和弹出)。我已经隔离了堆缓冲区溢出的原因,它发生在
memcpy
。
#define DYNAMIC_STARTING_SIZE 1
#define DYNAMIC_GROWTH_AMOUNT 1.5
struct dynamic_array {
void* data;
size_t amount;
size_t capacity;
size_t element_size;
};
struct dynamic_array dynamic_array_create(size_t element_size) {
struct dynamic_array dynamic_array;
dynamic_array.data = malloc(sizeof(DYNAMIC_STARTING_SIZE * element_size));
dynamic_array.amount = 0;
dynamic_array.capacity = DYNAMIC_STARTING_SIZE;
dynamic_array.element_size = element_size;
return dynamic_array;
}
void* dynamic_array_get(struct dynamic_array* dynamic_array, size_t index) {
if (index >= dynamic_array->amount) {
return NULL;
}
return ((unsigned char*)dynamic_array->data) + (index * dynamic_array->element_size);
}
void dynamic_array_push(struct dynamic_array* dynamic_array,
const void* element) {
unsigned char* destination =
(unsigned char*)dynamic_array->data +
dynamic_array->amount * dynamic_array->element_size;
if (dynamic_array->amount >= dynamic_array->capacity) {
dynamic_array->capacity =
(size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
dynamic_array->data =
realloc(dynamic_array->data,
dynamic_array->capacity * dynamic_array->element_size);
}
memcpy((void*)destination, element, dynamic_array->element_size); /* Heap buffer overflow here */
dynamic_array->amount++;
}
我打印出了内存地址位置的值,它们的大小应该是
point_a
的大小,并在我的机器上获取了这些值:
Starting memory address: 0x502000000010
Destination memory address: 0x502000000010
Destination ending memory address: 0x502000000020 <- This is actually the address after the end
但是地址清理程序显示地址
const char* point_a
处的写入大小为 16 字节(因为 int
是 8 字节,两个 0x502000000018
在我的机器上也是 8 字节)。
然后,我决定删除
const char* data
字段,这解决了问题。然后我测试并发现任何指针,无论它在结构中的什么位置,都会导致堆溢出。
destination
应该是正确的,因为我正在获取数据数组位置,并添加数量乘以元素大小。 element
和 dynamic_array->element_size
位于正确的位置。当您第一次创建数组时,我也会适当地为数组数据分配内存,并在需要时重新分配(这在这种情况下并不重要,因为数组一旦创建就已经可以保存一个值)。
我还应该做些什么来支持用于动态数组的结构中的指针,或者这里还有其他问题吗?
与我的具体问题类似但未能得到答案的问题:
有关memcpy和动态数组的其他类似问题:
您的主要问题是创建初始数组的位置:
dynamic_array.data = malloc(sizeof(DYNAMIC_STARTING_SIZE * element_size));
您要求的是表达式
DYNAMIC_STARTING_SIZE * element_size
的数据类型的大小,即 size_t
,而不是表达式的值。 size_t
很可能是 8 个字节,因此大于此大小的元素将无法分配足够的空间。这会导致您写入超出分配内存的末尾。
您想要:
dynamic_array.data = malloc(DYNAMIC_STARTING_SIZE * element_size);
您也遇到了问题:
unsigned char* destination =
(unsigned char*)dynamic_array->data +
dynamic_array->amount * dynamic_array->element_size;
if (dynamic_array->amount >= dynamic_array->capacity) {
dynamic_array->capacity =
(size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
dynamic_array->data =
realloc(dynamic_array->data,
dynamic_array->capacity * dynamic_array->element_size);
}
如果重新分配移动了内存,则
destination
将指向无效位置。所以先检查尺寸:
if (dynamic_array->amount >= dynamic_array->capacity) {
dynamic_array->capacity =
(size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
dynamic_array->data =
realloc(dynamic_array->data,
dynamic_array->capacity * dynamic_array->element_size);
}
unsigned char* destination =
(unsigned char*)dynamic_array->data +
dynamic_array->amount * dynamic_array->element_size;