首页 > 解决方案 > 为什么类型双关语被认为是 UB?

问题描述

想象一下:

uint64_t x = *(uint64_t *)((unsigned char[8]){'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'});

读过这样的双关语是未定义的行为。为什么?从字面上看,我将 8 个字节的字节重新解释为 8 个字节的整数。我看不出这与 a 有什么不同,union除了类型双关语是未定义的行为而unions 不是?我亲自问过一个程序员同事,他们说如果你这样做,要么你知道你在做什么,要么你了一个错误。但是社区说应该始终避免这种做法?为什么?

标签: ccastingundefined-behaviortype-punning

解决方案


最终的原因是“因为语言规范是这样说的”。你不必为此争论。如果这就是语言的方式,那就是它的方式。

如果您想知道这样做的动机,那就是原始 C 语言缺乏任何表达两个左值不能相互别名的方式(并且restrict大多数语言用户仍然几乎不理解现代语言的关键字) . 不能假设两个左值不能别名意味着编译器不能重新排序加载和存储,并且必须为每次访问对象时实际执行从/到内存的加载和存储,而不是将值保存在寄存器中,除非它知道对象的地址从未被占用。

C 的基于类型的别名规则通过让编译器假定具有不同类型的左值不会别名,在一定程度上缓解了这种情况。

另请注意,在您的示例中,不仅有类型双关语,还有错位。该unsigned char数组没有固有的对齐方式,因此uint64_t在该地址访问 a 将是一个对齐错误(UB 出于另一个原因),与任何别名规则无关。


推荐阅读