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 . . .
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;
9条答案
按热度按时间zbwhf8kr1#
看起来您希望
int
和unsigned int
是16位整数,但显然不是这样,最有可能的是,它是32位整数--足够大,可以避免您所期望的回绕。请注意,没有完全符合C语言的方法来实现这一点,因为超出范围的值的有符号/无符号转换是由实现定义的。但在大多数情况下,这仍然有效:
或者可替换地:
yfjy0ee72#
我知道这是个老问题,但这是个好问题,那么这个怎么样?
这样做是因为我们将
x
的地址转换为它的类型的带符号版本,这是C标准允许的。不是所有像这样的类型双关语(事实上大多数)都是法律的的。标准是这样说的。对象的储存值应该只能由具有下列其中一种型别的左值存取:
所以,唉,由于我们访问
x
的位时,就好像它们是带符号的(通过指针),实际的转换操作被替换为阅读看起来只是一个带负数号的短整型,并且转换顺利进行。然而,这在一个补码机器上可能会出错,但这些非常非常罕见,非常过时。我都懒得去照顾他们。wgmfuz8q3#
@Mysticial知道了,一个短的通常是16位的,会说明答案:
更详细一点。这是关于带符号的数字是如何存储在内存中的。搜索二进制补码表示法以了解更多详细信息,但这里是基础知识。
我们来看一下十进制的65529,它可以用十六进制表示为
FFF9h
,也可以用二进制表示为:11111111 11111001
当我们声明
short zz = 65529;
时,编译器将65529解释为一个有符号值。在二进制补码表示法中,最高位表示一个有符号值是正还是负。在本例中,你可以看到最高位是一个1
,因此它被视为一个负数。这就是为什么它输出-7
。对于一个
unsigned short
,我们不关心符号,因为它是unsigned
,所以当我们用%d
打印它时,我们使用了所有的16位,所以它被解释为65529
。ve7v8dk24#
要理解其中的原因,您需要知道CPU使用2的补码表示有符号数(可能不是全部,而是许多)。
另外,int和unsigned int类型的大小可能会因CPU的不同而不同。在执行以下特定操作时:
6l7fqoea5#
值65529 u和-7在16位整型中的表示是相同的,只是位的解释不同。
对于更大的int和这些值,需要sign extend;一种方法是使用逻辑运算
如果速度不是问题,或者除法在处理器上很快,
乘法左移16位(同样,假设16到32扩展),除法右移,保持符号不变。
r6vfmomb6#
你期望你的
int
类型是16位宽,在这种情况下你得到的实际上是一个负值。但它最有可能是32位宽,所以一个有符号的int
可以很好地表示65529。你可以通过打印sizeof(int)
来检查这一点。l7mqbcuq7#
要回答上面评论中的问题,请尝试以下内容:
或
lmvvr0a88#
由于转换无符号值用来表示正数,因此可以通过将最高有效位设置为0来完成转换。因此,程序不会将其解释为二进制补码值。一个警告是,这将丢失接近无符号类型max的数字的信息。
mcvgt66p9#
我知道这是一个老问题,但我认为响应者可能误解了它。我认为它的目的是将接收到的16位位序列转换为无符号整数(技术上讲,是
unsigned short
)转换为有符号整数。当你需要将从网络接收到的数据从网络字节顺序转换为主机字节顺序时,可以使用union:现在
minus_seven
的值为-7。