请参阅此代码段
int main() { unsigned int a = 1000; int b = -1; if (a>b) printf("A is BIG! %d\n", a-b); else printf("a is SMALL! %d\n", a-b); return 0; }
这给出了输出:a是SMALL:1001
我不明白这里发生了什么.>运算符如何在这里工作?为什么"a"小于"b"?如果它确实更小,为什么我得到一个正数(1001)作为差异?
不同的积分类型之间的二进制操作由所谓的定义的"公共"型内进行通常的算术转换(参见语言规范,6.3.1.8).在你的情况下,"常见"类型是unsigned int
.这意味着int
操作数(您的b
)将unsigned int
在比较之前转换为以及执行减法的目的.
当-1
被转换为unsigned int
结果是最大可能unsigned int
值(同UINT_MAX
).毋庸置疑,它将大于你的无符号1000
值,这意味着它a > b
确实是错误的,a
并且确实比较小(unsigned) b
.在if
你的代码应该下定决心else
分支,它是你在实验中观察到的东西.
相同的转换规则适用于减法.你a-b
的确被解释为a - (unsigned) b
,结果有类型unsigned int
.这样的值不能使用%d
格式说明符打印,因为%d
只能使用有符号值.您尝试打印它会%d
导致未定义的行为,因此从C语言的角度来看,您看到的打印值(即使它在实践中具有逻辑确定性解释)完全没有意义.
编辑:实际上,我对未定义的行为部分可能是错的.根据C语言规范,相应的有符号和无符号整数类型范围的公共部分应具有相同的表示(根据脚注31,"可互换性作为函数的参数").因此,a - b
表达式的结果是1001
如上所述的无符号,除非我遗漏了某些内容,否则使用%d
说明符打印此特定无符号值是合法的,因为它属于正范围int
.打印(unsigned) INT_MAX + 1
时%d
未定义,但1001u
很好.
在int
32位的典型实现中,转换为a时的-1 unsigned int
是4,294,967,295,其确实≥1000.
即使你在一个unsigned
世界中对待减法,1000 - (4,294,967,295) = -4,294,966,295 = 1,001
这就是你得到的.
这就是为什么gcc
当你比较会吐出警告unsigned
有signed
.(如果没有看到警告,请传递-Wsign-compare
旗帜.)