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

时间:2013-10-21 09:06:13

标签: c casting int unsigned htonl

我想通过套接字传递一个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;

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

3 个答案:

答案 0 :(得分:25)

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

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

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

通过指针强制转换,即

uint32_t u = *(uint32_t*)&x;

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

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

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

答案 1 :(得分:6)

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

的内容
(float) 3

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

(float) 3.0

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

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

请注意,理论上,可能不会使用2的补码,unsignedsigned可能会有非常不同的表示形式,在这种情况下实际的位模式可能会发生变化。

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

  

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

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

答案 2 :(得分:2)

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

  

7.20.1.1精确宽度整数类型typedef名称intN_t指定一个有符号整数类型,其宽度为N,没有填充位,以及两个   补充表示。因此,int8_t表示这样的有符号整数   类型,宽度恰好为8位。

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

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

相关问题