30

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

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

x和each中的字节是否总是u完全相同?怎么回滚:

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

我意识到负值转换为大的无符号值,但这并不重要,因为我只是在另一端回退。但是,如果强制转换与实际字节混淆,那么我不能确定强制转换会返回相同的值。

4

3 回答 3

28

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

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

对于完全可移植性(这可能会有点过分),您需要使用类型双关语而不是转换。这可以通过以下两种方式之一完成:

通过指针强制转换,即

uint32_t u = *(uint32_t*)&x;

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

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

这也可以用于例如转换 from doubleto uint64_t,如果你想避免未定义的行为,你可能不会使用指针强制转换。

于 2013-10-21T09:36:31.760 回答
6

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

(float) 3

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

(float) 3.0

这是一种类型消歧。

假设一个 2 的补码表示(见下面的注释),当你转换一个inttounsigned int时,位模式没有改变,只有它的语义含义;如果你把它扔回去,结果将永远是正确的。它属于类型消歧的情况,因为没有更改任何位,只有计算机解释它们的方式。

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

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

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

我想说的是,在实践中,你可以假设它是安全的。

于 2013-10-21T09:18:51.543 回答
2

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

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

uint32_t从理论上讲,从to的反向转换int32_t是实现定义的,就像所有unsignedsigned转换一样。但我无法想象一个平台会与你所期望的不同。

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

于 2013-10-21T10:04:29.717 回答