基于void*数组的ArrayList存储函数指针出现内存访问错误求助
问题分析与修复:存储函数指针的ArrayList访问违例崩溃
核心错误点
- 元素地址计算错误:在
arraylist_add和main中,错误将payload转为u64*后做指针偏移,这会强制按8字节步长移动,但函数指针的实际大小(即stride)可能不等于8字节(比如32位系统下是4字节),最终导致内存越界或读取错误地址。 - 扩容浮点运算精度问题:使用
list->capacity *1.75的浮点计算方式,可能因精度丢失导致新容量计算偏差,建议改用整数运算避免此问题。
修复后的完整代码
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; typedef struct { u32 capacity; u32 used; u64 stride; void* payload; } ArrayList; ArrayList* arraylist_create(u64 stride) { ArrayList* array = malloc(sizeof(ArrayList)); if (array == NULL) { printf("%s", "Failed to malloc(sizeof(ArrayList))"); return NULL; } array->capacity = 1; array->stride = stride; array->used = 0; array->payload = malloc(stride); if (array->payload == NULL) { free(array); printf("%s", "Failed to malloc payload"); return NULL; } return array; } void arraylist_add(ArrayList* list, void* payload) { if (list == NULL || payload == NULL) return; // 改用整数运算计算新容量,避免浮点精度丢失 if (list->used == list->capacity) { u32 new_capacity = (list->capacity * 7) / 4 + 1; // 等价于1.75倍容量+1 void* block = malloc(list->stride * new_capacity); if (block == NULL) { printf("%s", "Failed to realloc payload"); return; } memcpy(block, list->payload, list->stride * list->used); free(list->payload); list->payload = block; list->capacity = new_capacity; } // 用char*计算偏移,确保按字节正确定位元素 char* base = (char*)list->payload; memcpy(base + list->used * list->stride, payload, list->stride); list->used++; } void fun1(void) { printf("%s\n", "fun1"); } void fun2(void) { printf("%s\n", "fun2"); } typedef void (*pfn)(void); int main(void) { ArrayList* array = arraylist_create(sizeof(pfn)); if (array == NULL) return 1; arraylist_add(array, &fun1); // 显式取函数地址,代码更清晰 arraylist_add(array, &fun2); // 用char*定位每个函数指针的起始地址 char* base = (char*)array->payload; for (u8 i = 0; i < array->used; ++i) { pfn function = *(pfn*)(base + i * array->stride); function(); } // 释放内存避免泄漏 free(array->payload); free(array); return 0; }
关键修复说明
- 地址计算修正:将
payload转为char*后,通过i * stride计算字节偏移,确保无论stride是4还是8字节,都能精准定位每个元素。 - 扩容逻辑优化:用整数运算
(list->capacity *7)/4 +1替代浮点计算,保证容量计算的准确性,避免精度丢失问题。 - 内存安全增强:添加malloc失败检查和内存释放逻辑,避免内存泄漏。
- 代码可读性提升:显式使用
&fun1传递函数地址,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Bruno Araújo
相关产品推荐
相关产品推荐

