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

Fla*_*ash 27 c int unsigned casting htonl

我想x通过套接字传递一个32位有符号整数.为了让接收器知道预期的字节顺序,我htonl(x)在发送之前调用.htonl期待一个,uint32_t但我想确定当我把我int32_t带到一个时会发生什么uint32_t.

int32_t x = something;
uint32_t u = (uint32_t) x;
Run Code Online (Sandbox Code Playgroud)

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

uint32_t u = something;
int32_t x = (int32_t) u;
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*oph 26

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

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

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

通过指针转换,即

uint32_t u = *(uint32_t*)&x;
Run Code Online (Sandbox Code Playgroud)

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

uint32_t u = ((union { int32_t i; uint32_t u; }){ .i = x }).u;
Run Code Online (Sandbox Code Playgroud)

如果你想避免未定义的行为,它也可以用于转换doubleuint64_t,如果你不想使用指针转换.

  • @Zulan:类型的有符号和无符号版本可以别名(参见C11第6.5节第7节) (2认同)

Fil*_*ves 6

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

(float) 3
Run Code Online (Sandbox Code Playgroud)

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

(float) 3.0
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

  • 最初的例子是错误的,因为`3.0`是一个`double`,表达式`(float)3.0`肯定也是一种类型转换. (2认同)