![](/img/trans.png)
[英]C signed arithmetic: difference in representation between literals and variables
[英]What is the difference between literals and variables in C (signed vs unsigned short ints)?
我在“ 计算机系统:程序员的角度 ”一书中看到了以下代码,2 / E. 这很好用,并创建所需的输出。 输出可以通过有符号和无符号表示的区别来解释。
#include<stdio.h>
int main() {
if (-1 < 0u) {
printf("-1 < 0u\n");
}
else {
printf("-1 >= 0u\n");
}
return 0;
}
上面的代码产生-1 >= 0u
,但是,下面的代码应该与上面相同,不会! 换一种说法,
#include <stdio.h>
int main() {
unsigned short u = 0u;
short x = -1;
if (x < u)
printf("-1 < 0u\n");
else
printf("-1 >= 0u\n");
return 0;
}
产量-1 < 0u
。 为什么会这样? 我无法解释这一点。
请注意,我已经看到像类似的问题这样 ,但他们不帮助。
PS。 正如@Abhineet所说,这种困境可以通过将short
更改为int
来解决。 但是,怎么能解释这种现象呢? 换句话说,4个字节中的-1
是0xff ff ff ff
,2个字节中的-1
是0xff ff
。 将它们作为2s补码解释为unsigned
,它们具有4294967295
和65535
对应值。 它们都不小于0
,我认为在这两种情况下,输出都需要为-1 >= 0u
,即x >= u
。
它在小端英特尔系统上的示例输出:
简而言之:
-1 < 0u
u =
00 00
x =
ff ff
对于int:
-1 >= 0u
u =
00 00 00 00
x =
ff ff ff ff
上面的代码产生-1> = 0u
所有整数文字 (数字常量 )都有一个类型,因此也有一个签名。 默认情况下,它们是int
类型,已签名。 附加u
后缀时,将文字转换为unsigned int
。
对于任何C表达式,其中有一个已签名的操作数和一个未被取消的操作数,balacing规则(正式地说: 通常的算术转换 )会隐式地将签名类型转换为unsigned。
从有符号到无符号的转换是明确定义的(6.3.1.3):
否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。
例如,对于标准二进制补码系统上的32位整数,无符号整数的最大值为2^32 - 1
(4294967295,limit.h中的UINT_MAX)。 超过最大值的是2^32
。 并且-1 + 2^32 = 4294967295
,因此文字-1
转换为unsigned int,值为4294967295
。 哪个大于0。
但是,当您将类型切换为short时,最终会得到一个小整数类型 。 这是两个例子之间的区别。 只要小整数类型是表达式的一部分, 整数提升规则就会隐式地将其转换为更大的int(6.3.1.1):
如果int可以表示原始类型的所有值(由宽度限制,对于位字段),则该值将转换为int; 否则,它将转换为unsigned int。 这些被称为整数促销。 整数促销不会更改所有其他类型。
如果short
小于给定平台上的int
(就像在32位和64位系统上的情况那样),那么任何short
或unsigned short
都将总是转换为int
,因为它们可以放在一个中。
因此,对于表达式if (x < u)
,实际上最终会得到if((int)x < (int)u)
,其行为符合预期(-1小于0)。
您正在使用C的整数提升规则。
类型小于int
运算符会自动将其操作数提升为int
或unsigned int
。 有关详细说明,请参阅注释。 如果类型在此之后仍然不匹配(例如unsigned int与int),则二进制(双操作数)运算符还有一个步骤。 我不会试图更详细地总结规则。 见Lundin的回答 。
这篇博文更详细地介绍了这一点,与您的类似示例:signed和unsigned char。 它引用了C99规范:
如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int。 这些被称为整数促销。 整数促销不会更改所有其他类型。
你可以在诸如godbolt之类的东西上更容易地玩这个, 其功能是返回一个或零 。 只需查看编译器输出即可查看最终发生的情况。
#define mytype short
int main() {
unsigned mytype u = 0u;
mytype x = -1;
return (x < u);
}
除了你似乎假设的,这不是类型的特定宽度的属性,这里是2字节对4字节,而是要应用的规则的问题。 整数提升规则指出short
和unsigned short
转换为int
上,其中的值的相应范围适合所有平台int
。 由于这是这种情况,因此保留两个值并获取int
类型。 -1
在int
完全可表示为0
。 因此-1
的测试结果小于0
。
在针对0u
测试-1
的情况下,公共转换选择unsigned
类型作为转换两者的公共类型。 -1
转换为unsigned
是值UINT_MAX
,大于0u
。
这是一个很好的例子,为什么你不应该使用“窄”类型来进行算术或比较。 仅在具有服务器大小约束时才使用它们。 对于简单变量,这种情况很少发生,但主要是对于大型数组,您可以通过窄类型存储获得真正的数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.