C语言中将指针强制转换为(void**)是否属于良定义行为?
结论
你代码里的(void**)&p强制类型转换不属于C标准下的良定义操作,是明确的未定义行为,不建议在可移植代码里使用。
原因拆解
很多人会把void*的通用指针规则错套到void**上,这是最常见的误区:
- C标准只对
void*做了特殊兼容规定:所有指向对象的指针都可以和void*互相转换,转换回原类型时值保持不变。但void**没有任何特殊待遇,它就是一个普通的指针类型,指向的对象类型是void*,不存在所谓“通用二级指针”的说法。 - 关于对齐要求:C标准从未强制规定不同类型的指针必须有相同的对齐要求、相同的内存表示。
struct A*和void*是完全不同的类型,它们的对齐要求可能一致,也可能void*对齐要求更严,也可能结构体指针对齐要求更严——不管哪种情况,把struct A**强转为void**传参,首先就违反了严格别名规则:函数f拿到参数后,会向这个地址写入一个void*类型的值,但这个地址实际对应的对象是struct A*类型,这种跨类型的对象访问本身就是未定义行为,和对齐是否匹配没有必然关系。
你提到的SEI C编码规范里“不要转换为对齐要求更严格的指针”只是未定义行为的触发场景之一,哪怕两种类型对齐要求刚好完全一致,只要违反严格别名规则,代码行为就不受标准保障,编译器可能做出意料之外的优化,运行时也可能出现崩溃、值错乱的问题。
符合标准的正确写法
不要做二级指针的强制转换,用一个void*类型的临时变量中转即可,全程没有任何违规转换:
struct A* p; void* tmp; f(sizeof(struct A), &tmp); p = tmp;
这个写法的合法性是标准明确保障的:&tmp是原生的void**类型,和函数f的形参类型完全匹配,函数向tmp写入分配的内存地址完全合法;后续把void*类型的tmp赋值给struct A*类型的p,属于void*到其他对象指针的标准转换,不存在任何未定义行为。
内容的提问来源于stack exchange,提问作者user18676624
相关产品推荐
相关产品推荐

