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
相关产品推荐
相关产品推荐

