使用联合体进行指向有/无符号类型指针的类型双关是否符合标准?
首先直接给出结论:你代码里通过联合体访问p.u的行为不被C标准保证有效,属于未定义行为,但在绝大多数实际平台上确实能正常工作。
下面详细拆解原因:
联合体类型双关的规则边界:C标准允许通过联合体的不同成员访问同一块内存,但仅当两个成员的类型是兼容类型,或是同一基础类型的有符号/无符号版本(比如
int和unsigned int)时,这种跨成员读取才是定义明确的。但这里涉及的是指针类型,不是基础整数类型,规则不适用。指针类型的兼容性问题:
int*和unsigned*是完全不同的指针类型,C标准不认为它们兼容。所以你先给p.i赋值,再读取p.u的操作,属于通过不兼容类型的联合体成员访问内存,这种行为是标准定义的未定义行为——编译器可以任意处理,比如优化掉这个操作、生成错误的机器码,或者刚好按你预期工作。内存表示的标准要求:C标准没有强制要求
int*和unsigned*的内存表示必须一致。虽然在x86、ARM等几乎所有主流平台上,这两种指针的存储格式完全相同,但这是平台实现的特性,不是标准的硬性规定。理论上存在特殊平台,这两种指针的编码方式、存储长度可能不同,只是这种平台非常罕见。更安全的替代写法:如果要合法完成这类转换,建议用显式的指针类型转换:
foo((unsigned*)&num);
这里要补充说明:虽然转换指针类型是允许的,但后续通过unsigned*访问原本的int对象时,因为int和unsigned int是对应有符号/无符号类型,且你的num是非负数(值在两种类型的可表示范围内),这种访问是符合类型别名规则例外的,属于定义明确的行为。
内容的提问来源于stack exchange,提问作者martinkunev

