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

基于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;
}

关键修复说明

  1. 地址计算修正:将payload转为char*后,通过i * stride计算字节偏移,确保无论stride是4还是8字节,都能精准定位每个元素。
  2. 扩容逻辑优化:用整数运算(list->capacity *7)/4 +1替代浮点计算,保证容量计算的准确性,避免精度丢失问题。
  3. 内存安全增强:添加malloc失败检查和内存释放逻辑,避免内存泄漏。
  4. 代码可读性提升:显式使用&fun1传递函数地址,逻辑更清晰。

内容的提问来源于stack exchange,提问作者Bruno Araújo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:13