25

我正在尝试65529unsigned inta 转换为已签名的int. 我试着做这样的演员:

unsigned int x = 65529;
int y = (int) x;

但是y当它应该返回 -7 时仍然返回 65529。这是为什么?

4

9 回答 9

37

看起来您期望int并且unsigned int是一个 16 位整数。显然情况并非如此。最有可能的是,它是一个 32 位整数 - 它足够大,可以避免您所期望的环绕。

请注意,没有完全符合 C 标准的方法来执行此操作,因为超出范围的有符号/无符号值之间的转换是实现定义的。但这在大多数情况下仍然有效:

unsigned int x = 65529;
int y = (short) x;      //  If short is a 16-bit integer.

或者:

unsigned int x = 65529;
int y = (int16_t) x;    //  This is defined in <stdint.h>
于 2011-11-29T20:30:35.173 回答
6

我知道这是一个老问题,但这是一个很好的问题,那么这个怎么样?

unsigned short int x = 65529U;
short int y = *(short int*)&x;

printf("%d\n", y);
于 2014-01-14T18:57:20.803 回答
4

@Mysticial 明白了。短片通常是 16 位的,将说明答案:

int main()  
{
    unsigned int x = 65529;
    int y = (int) x;
    printf("%d\n", y);

    unsigned short z = 65529;
    short zz = (short)z;
    printf("%d\n", zz);
}

65529
-7
Press any key to continue . . .


再详细一点。这完全是关于如何将带符号的数字存储在内存中。搜索二进制补码符号以获取更多详细信息,但这里是基础知识。

所以让我们看一下十进制的 65529。它可以用FFF9h十六进制表示。我们也可以用二进制表示:

11111111 11111001

当我们声明short zz = 65529;时,编译器将 65529 解释为有符号值。在二进制补码表示法中,最高位表示有符号值是正数还是负数。在这种情况下,您可以看到最高位是 a 1,因此将其视为负数。这就是它打印出来的原因-7

对于 an unsigned short,我们不关心符号,因为它是unsigned。因此,当我们使用 打印出来时%d,我们使用所有 16 位,所以它被解释为65529.

于 2011-11-29T20:34:35.590 回答
2

要了解原因,您需要知道 CPU 使用二进制补码表示有符号数(可能不是全部,而是很多)。

    byte n = 1; //0000 0001 =  1
    n = ~n + 1; //1111 1110 + 0000 0001 = 1111 1111 = -1

而且,类型 int 和 unsigned int 的大小可以不同,具体取决于您的 CPU。当做这样的特定事情时:

   #include <stdint.h>
   int8_t ibyte;
   uint8_t ubyte;
   int16_t iword;
   //......
于 2015-03-21T11:13:09.360 回答
1

对于 16 位整数,值 65529u 和 -7 的表示是相同的。只有位的解释不同。

对于较大的整数和这些值,您需要签署扩展;一种方法是使用逻辑运算

int y = (int )(x | 0xffff0000u); // assumes 16 to 32 extension, x is > 32767

如果速度不是问题,或者处理器上的除法速度很快,

int y = ((int ) (x * 65536u)) / 65536;

乘法左移 16 位(再次假设 16 到 32 扩展),除法右移保持符号。

于 2011-11-29T20:31:43.160 回答
0

您期望您的int类型是 16 位宽,在这种情况下,您确实会得到一个负值。但很可能它是 32 位宽,所以有符号int可以代表 65529 就好了。您可以通过打印来检查这一点sizeof(int)

于 2011-11-29T20:31:52.050 回答
0

要回答上面评论中发布的问题 - 尝试这样的事情:

unsigned short int x = 65529U;
short int y = (short int)x;

printf("%d\n", y);

或者

unsigned short int x = 65529U;
short int y = 0;

memcpy(&y, &x, sizeof(short int);
printf("%d\n", y);
于 2011-11-29T20:35:14.543 回答
0

由于转换无符号值用于表示正数,因此可以通过将最高有效位设置为 0 来完成转换。因此,程序不会将其解释为二进制补码值。一个警告是,这将丢失接近无符号类型最大值的数字的信息。

template <typename TUnsigned, typename TSinged>
TSinged UnsignedToSigned(TUnsigned val)
{
    return val & ~(1 << ((sizeof(TUnsigned) * 8) - 1));
}
于 2019-03-01T06:46:54.467 回答
0

我知道这是一个老问题,但我认为响应者可能误解了它。我认为打算将作为无符号整数(技术上是 an unsigned short)接收的 16 位位序列转换为有符号整数。当您需要将从网络接收到的内容从网络字节顺序转换为主机字节顺序时,可能会发生这种情况(它最近发生在我身上)。在这种情况下,使用联合:

unsigned short value_from_network;
unsigned short host_val = ntohs(value_from_network);
// Now suppose host_val is 65529.
union SignedUnsigned {
  short          s_int;
  unsigned short us_int;
};
SignedUnsigned su;
su.us_int = host_val;
short minus_seven = su.s_int;

现在minus_seven的值为 -7。

于 2020-03-19T20:41:32.367 回答