C语言指针类型转换层级解析及结构体指针数组泛型函数传参疑问
咱们一步步拆解你遇到的这个指针类型转换问题,先把你的代码场景再理一遍,你就能彻底搞懂为啥两种强转的结果不一样了。
首先先把你写的核心代码贴出来,方便对照:
typedef struct { int elem1; char *elem2; // ... 其他成员 } example_t; #define MAX_SIZE 100 example_t *array[MAX_SIZE]; // 泛型添加函数 void entryAdd(void *new_entry, const void *any_array[], const int max_entries);
为啥第一次直接传参array会出警告?
你第一次直接写entryAdd(&example, array, MAX_ITEMS);时,编译器报expected 'const void **' but argument is of type 'example_t **',这本质是C语言的指针类型安全规则在起作用:
array是结构体指针数组,作为函数参数时会退化为example_t**类型(指向第一个example_t*元素的指针);- 而函数的第二个形参
const void *any_array[],实际会被编译器解析为const void**(指向const void*的指针); - C语言不允许
example_t**和const void**之间的隐式转换,因为这涉及到多层指针的const限定安全性,后面我会举例子说明。
两种强制转换的本质区别
你尝试的两种强转,本质是对指针的转换层级完全不同,咱们逐个说:
1. 强转成(void*) array
你猜的方向有点偏,其实这里的array是example_t**类型,强转成void*的意思是:把整个example_t**类型的指针,直接转换成通用的void*指针——也就是说,你把“指向结构体指针的指针”,当成了一个“无类型的单个指针”来传递。
为啥这样编译没警告?因为C语言规定,任何对象指针都可以隐式转换成void*,而当你把这个void*传给期望const void**的形参时,编译器会把void*当作一个普通指针,自动给它加上const限定(因为const是更严格的权限限制,这种转换是安全的),所以最终匹配了const void**的形参要求,就没警告了。
2. 强转成(void**) array
这个强转的意思是:把example_t**类型的指针,直接转换成void**类型——也就是把“指向example_t*的指针”,改成“指向void*的指针”。
这时候编译器报expected 'const void **' but argument is of type 'void **',原因还是刚才说的多层指针const安全问题:void**是指向void*的指针,而const void**是指向const void*的指针,C语言不允许这两种类型的隐式转换。举个简单的反例就能懂为啥:
void* p; const void** pp = &p; // 如果允许这种转换 const char c = 'a'; *pp = &c; // 把const char*赋值给了void*的指针 // 这时候p指向了一个const变量,但p本身是无const限定的,后续可以通过p修改这个const变量,直接破坏了const的安全性
所以编译器会严格阻止这种可能破坏类型安全的隐式转换,因此给出警告。
给你的正确传参建议
既然函数形参是const void**类型,最规范也最安全的做法,就是直接把array强转成const void**:
entryAdd(&example, (const void**)array, MAX_ITEMS);
这样类型完全匹配,既没有警告,也符合C语言的类型安全规则。
内容来源于stack exchange

