signed和unsigned int之间的强制转换是否在内存中保持变量的精确位模式?

我想通过套接字传递一个32位有符号整数x 。 为了让接收器知道期望哪个字节顺序,我在发送之前调用htonl(x) 。 虽然htonl需要一个uint32_t但我想确定当我将int32_t转换为uint32_t时会发生什么。

 int32_t x = something; uint32_t u = (uint32_t) x; 

总是这样的情况, xu中的字节将完全相同吗? 怎么回事:

 uint32_t u = something; int32_t x = (int32_t) u; 

我意识到负值会转换为大的无符号值,但这并不重要,因为我只是回到了另一端。 但是如果使用实际字节进行混乱,那么我无法确定返回将返回相同的值。

通常,C中的转换是根据值而不是位模式指定的 – 前者将被保留(如果可能),但后者不一定如此。 如果没有填充的二进制补码表示 – 这对于固定的整数类型是强制性的 – 这种区别无关紧要,并且强制转换确实是一个noop。

但即使从有符号到无符号的转换会改变位模式,再次将其转换回来也会恢复原始值 – 需要注意的是,超出范围的无符号到有符号转换是实现定义的并且可能会引发信号溢出。

为了完全可移植性(可能是过度杀伤),您需要使用类型惩罚而不是转换。 这可以通过以下两种方式之一完成:

通过指针强制转换,即

 uint32_t u = *(uint32_t*)&x; 

你应该小心,因为它可能违反有效的打字规则(但对整数类型的有符号/无符号变体是好的)或通过工会,即

 uint32_t u = ((union { int32_t i; uint32_t u; }){ .i = x }).u; 

它也可以用于例如从double转换为uint64_t ,如果你想避免未定义的行为,你可能不会使用指针转换。

在C中使用强制转换表示“类型转换”和“类型消歧”。 如果你有类似的东西

 (float) 3 

然后是类型转换,实际位改变。 如果你说

 (float) 3.0 

这是一种消除歧义的类型。

假设2的补码表示 (见下面的注释),当你将intunsigned int ,位模式不会改变,只会改变它的语义; 如果你把它丢回来,结果总是正确的。 它属于类型消歧的情况,因为没有位被更改,只有计算机解释它们的方式。

注意,理论上,可以不使用2的补码,并且unsigned和有signed可以具有非常不同的表示,并且在这种情况下实际的位模式可以改变。

但是,从C11(当前的C标准),你实际上保证sizeof(int) == sizeof(unsigned int)

(§6.2.5/ 6)对于每个有符号整数类型,有一个相应的(但不同的)无符号整数类型(用关键字unsigned指定),它使用相同的存储量(包括符号信息)并具有相同的对准要求[…]

我会说在实践中,你可以认为它是安全的。

这应该始终是安全的,因为如果intXX_t类型存在,则保证它们是二进制补码:

7.20.1.1精确宽度整数类型typedef名称intN_t指定有符号整数类型,其宽度为N,无填充位和二进制补码表示。 因此,int8_t表示这样的带符号整数类型,其宽度恰好为8位。

从理论上讲,从uint32_tint32_t的反向转换是实现定义的,对于所有unsignedsigned转换。 但我无法想象一个平台会做出与你期望不同的事情。

如果你想真正确定这一点,你仍然可以手动进行转换。 您只需要测试> INT32_MAX的值,然后进行一些数学运算。 即使你系统地这样做,一个体面的编译器应该能够检测并优化它。