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

C语言中realloc返回void*赋值二级指针为何合法

问题相关代码背景

以下代码来自Zed Shaw所著的Learn C The Hard Way第34个练习,要求实现本质为结构体封装指针数组的动态数组:

动态数组结构体定义:

typedef struct DArray {
    int end;
    int max;
    size_t element_size;
    size_t expand_rate;
    void **contents;
};

初始化函数DArray_create()实现:

DArray *DArray_create(size_t element_size, size_t initial_max)
{
    DArray *array = malloc(sizeof(DArray));
    array->max = initial_max;

    array->contents = calloc(initial_max, sizeof(void *));

    array->end = 0;
    array->element_size = element_size;
    array->expand_rate = 300;

    return array;
}

数组扩容函数DArray_resize()实现(原代码内存检查逻辑已省略):

static inline int DArray_resize(DArray *array, size_t newsize)
{
    array->max = newsize;

    void *contents = realloc(array->contents, array->max * sizeof(void *));

    array->contents = contents;

    return 0;
}

问题解答

核心疑问点:为什么realloc返回void *类型,可以直接赋值给void **类型的结构体成员,两种接收返回值的写法都能正常运行?

  • 这是C标准明确规定的合法行为:void *是C语言中的通用对象指针类型,可以和任意其他对象指针类型做隐式双向转换,不需要手动加强制类型转换,转换过程不会修改地址值,也不会产生运行时错误。
  • malloc/calloc/realloc三个内存分配函数的返回值统一设计为void *,原因是内存分配器只负责按字节数分配连续内存,不感知调用方要把这块内存存储什么类型的数据,通用返回值可以适配所有类型的指针赋值场景。
  • 你在DArray_create中直接把calloc的返回值赋值给void **类型的contents成员,本质已经触发了void *到void **的隐式转换,不是calloc特殊返回了void **类型。
  • DArray_resize中用void *类型的中间变量接收realloc返回值,再赋值给contents成员时,只是把隐式转换的步骤从realloc返回时挪到了中间变量赋值给成员的时刻,地址值全程没有变化,指向的都是realloc调整后的内存块,自然运行正常。
  • 如果你直接用void **类型变量接收realloc的返回值,本质是在接收返回值时就完成了void *到void **的隐式转换,和中间变量写法的效果完全一致,没有任何功能差异。

注意:这个隐式转换规则仅适用于纯C环境的对象指针,不适用于函数指针,也不适用于C环境。C要求void*必须经过强制类型转换才能赋值给其他类型指针,纯C代码中不需要做这个多余的强转。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:03:29