将Derived不透明结构体传入void*函数后转成首个成员是否合法?
结论:这段代码不符合C语言标准,属于未定义行为,且逻辑完全错误
核心原因分析
你的代码混淆了「结构体首个成员的类型」和「成员指向的类型」,导致转换逻辑彻底错误:
- 结构体内存布局:
struct Derived的首个成员是Base*类型的指针,而非Base结构体本身。这意味着Derived对象的起始地址处,存放的是一个指向Base结构体的指针值,而不是Base结构体的实际数据。 - 转换后的指针指向错误:
- 当
Derived* d被转成void* ptr传入函数后,ptr指向的是Derived对象的起始位置。 - 强制转换
(Base*)ptr得到的base指针,实际指向的是Derived对象内部的base成员(那个Base*变量)的内存地址,而非真正的Base结构体。 - 调用
base_set_first(base, 1)时,会把这个Base*变量的内存(原本是一个指针值)当成Base结构体的first成员来赋值,直接破坏了Derived对象里的指针,后续访问d->base会变成野指针,必然引发错误。
- 当
符合标准的正确做法
如果要通过void*传递Derived对象并操作其内部的Base结构体,正确的方式是先将void*转回Derived*,再访问其base成员:
void function(void *ptr) { Derived *derived = (Derived*)ptr; base_set_first(derived->base, 1); //... }
补充C标准相关规则
C标准允许将结构体指针转换为其首个成员类型的指针(注意是成员本身的类型,不是成员指向的类型):
- 例如,如果
struct Derived的首个成员是Base结构体(而非指针):
那么struct Derived { Base base; };(Base*)&derived_obj是合法的,因为结构体的起始地址与首个成员的起始地址一致,这种转换符合C标准要求。
内容的提问来源于stack exchange,提问作者Florian Bailly
相关产品推荐
相关产品推荐

