Tag: type punning

如何在不违反严格别名规则的情况下合法地使用类型惩罚与联合在struct sockaddr的变体之间进行投射?

POSIX打算将struct sockaddr变量指针转换为可转换的,但是根据C标准的解释,这可能违反了严格的别名规则,因此违反了UB。 (请参阅下面的评论这个答案 。)我至少可以确认gcc可能至少存在问题:此代码打印Bug! 启用优化,并且Yay! 禁用优化: #include #include #include sa_family_t test(struct sockaddr *a, struct sockaddr_in *b) { a->sa_family = AF_UNSPEC; b->sin_family = AF_INET; return a->sa_family; // AF_INET please! } int main(void) { struct sockaddr addr; sa_family_t x = test(&addr, (struct sockaddr_in*)&addr); if(x == AF_INET) printf(“Yay!\n”); else if(x == AF_UNSPEC) printf(“Bug!\n”); return 0; } 在联机IDE上观察此行为。 要解决此问题, […]

在C中安全地将char *加倍

在我写的一个开源程序中,我正在从文件中读取二进制数据(由另一个程序编写)并输出整数,双精度数和其他各种数据类型。 其中一个挑战是它需要在两个端点的32位和64位机器上运行,这意味着我最终不得不做一些低级别的bit-twiddling。 我知道(非常)关于类型惩罚和严格别名的一点点,并且想要确保我正确地做事。 基本上,很容易从char *转换为各种大小的int: int64_t snativeint64_t(const char *buf) { /* Interpret the first 8 bytes of buf as a 64-bit int */ return *(int64_t *) buf; } 我有一组支持函数来根据需要交换字节顺序,例如: int64_t swappedint64_t(const int64_t wrongend) { /* Change the endianness of a 64-bit integer */ return (((wrongend & 0xff00000000000000LL) >> 56) | ((wrongend & 0x00ff000000000000LL) >> 40) […]

通过联合键入C和C ++中的结构

我用gcc和g ++编译了这个迂腐,我不会在任何一个中得到警告: #include #include #include struct a { struct a *next; int i; }; struct b { struct b *next; int i; }; struct c { int x, x2, x3; union { struct aa; struct bb; } u; }; void foo(struct b *bar) { bar->next->i = 9; return; } int main(int argc, char *argv[]) { […]

使用void *键入punning而不破坏C99中的严格别名规则

我最近遇到了严格的别名规则,但是我无法理解如何使用void *来执行类型惩罚而不违反规则。 我知道这违反了规则: int x = 0xDEADBEEF; short *y = (short *)&x; *y = 42; int z = x; 而且我知道我可以安全地使用C99中的联合进行类型惩罚: union{ int x; short y; } data; data.x = 0xDEADBEEF; data.y = 42; int z = data.x; 但是如何在C99中使用void *来安全地执行类型惩罚呢? 以下是正确的: int x = 0xDEADBEEF; void * helper = (void *)&x; short *y = (short *)helper; […]

具有“共同初始序列”的联合’双关语’结构:为什么C(99+)而不是C ++规定了’联合类型的可见声明’?

背景 关于通过union进行类型惩罚的大多数未实现或实现定义的性质的讨论通常引用以下位,在此通过@ecatmur( https://stackoverflow.com/a/31557852/2757035 ),对标准的豁免-layout struct具有成员类型的“公共初始序列”: C11( 6.5.2.3结构和联合成员 ; 语义 ): […]如果一个union包含几个共享一个公共初始序列的结构(见下文),并且如果union对象当前包含这些结构中的一个,则允许检查它们中任何一个的公共初始部分。 可以看到已完成的工会类型的声明 。 如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享共同的初始序列 。 C ++ 03( [class.mem] / 16 ): 如果POD-union包含两个或多个共享公共初始序列的POD结构,并且如果POD-union对象当前包含这些POD结构中的一个,则允许检查它们中的任何一个的公共初始部分。 如果对应的成员具有一个或多个初始成员的序列的布局兼容类型(并且对于位字段,具有相同的宽度),则两个POD结构共享共同的初始序列。 这两个标准的其他版本都有类似的语言; 从C ++ 11开始,使用的术语是标准布局而不是POD 。 由于不需要重新解释,这不是真正的类型惩罚,只是应用于union成员访问的名称替换。 针对C ++ 17(臭名昭着的P0137R1)的提议使得这种显式使用的语言就像“访问就像其他结构成员被提名一样”。 但请注意粗体 – “ 在任何地方都可以看到完整类型的联合声明 ” – C11中存在的条款,但在2003年,2011年或2014年的C ++草案中没有任何内容(几乎完全相同,但后来的版本取代了“ POD“使用新术语标准布局 )。 在任何情况下,在任何C ++标准的相应部分中都完全没有’ union类型位的可见声明。 @loop和@ Mints97,在这里 – https://stackoverflow.com/a/28528989/2757035 – 显示这一行在C89中也没有,首先出现在C99中,然后保留在C中(尽管如此,再一次,永远不会过滤到C ++)。 关于此的标准讨论 [剪断 […]

uint32_t和uint8_t的联合未定义的行为?

在这个答案的评论中,据说使用如下的联合将整数分割成它们的字节将是未定义的行为。 在那个地方给出的代码是相似的,虽然与此不相同,如果我更改了代码的未定义行为相关方面,请注意。 union addr { uint8_t addr8[4]; uint32_t addr32; }; 到目前为止,我认为这将是一个很好的方法来做像addr = {127, 0, 0, 1}; 并得到相应的uint32_t作为回报。 (我承认根据我的系统的字节顺序,这可能产生不同的结果。但问题仍然存在。) 这是未定义的行为吗? 如果是这样,为什么? (我不知道C ++中的UB是什么意思是访问非活动的联盟成员。 ) C99 在这一点上,C99显然非常接近C ++ 03。 C ++ 03 在联合中,最多一个数据成员可以在任何时间处于活动状态,也就是说,最多一个数据成员的值可以随时存储在并集中。 C ++ 03,第9.5(1)节,第162页 然而 如果POD-union包含几个共享一个共同初始序列的POD结构,则允许检查任何POD结构成员的公共初始序列同上。 如果两个POD-struct类型具有相同数量的非静态数据成员,则它们是布局兼容的,并且相应的非静态数据成员(按顺序)具有布局兼容类型 C ++ 03,第9.2(14)节,第157页 如果两种类型T1和T2是相同类型,则T1和T2是布局兼容类型。 C ++ 03,第3.9(11)节,第53页 结论 因为uint8_t[4]和uint32_t不是同一类型(我猜,一个严格的别名 )(加上两个都不是POD结构/联合)上面的确是UB? C ++ 11 请注意,聚合类型不包含联合类型,因为具有联合类型的对象一次只能包含一个成员。 C ++ 11,脚注46,第42页