C程序中无符号整数与负字面量比较的行为差异:foo(99)返回true而bar(99)返回false的原因探究
为什么foo(99)返回true而bar(99)返回false?
这个问题的核心在于C语言的常用算术转换规则,以及无符号/有符号整数之间的隐式转换行为,我们来逐个拆解:
先看bar函数(符合你的预期)
bar的参数是unsigned long a,比较表达式是a > -2L:
-2L是有符号long类型,而a是无符号long类型,两者的类型秩相同(long和unsigned long的秩一致)。- 根据C标准的转换规则:当有符号类型和同秩的无符号类型进行运算/比较时,有符号操作数会被转换为对应的无符号类型。
-2作为有符号long,转换为unsigned long时,会变成ULONG_MAX - 1(补码表示下,-2的无符号等价值就是该类型的最大值减1)。- 现在比较的是
99UL和ULONG_MAX - 1,显然99远小于后者,所以a > -2L的结果是false,返回0。这和你的分析完全一致。
再看foo函数(为什么返回true?)
foo的参数是unsigned int a,比较表达式是a > -2L:
-2L是有符号long类型,而a是unsigned int类型。这里long的类型秩比int(包括unsigned int)更高(C语言中整数类型秩的顺序是:int<long<long long)。- 根据转换规则:当有符号类型的秩高于无符号类型时,无符号操作数会被转换为该有符号类型——只要无符号的值能被这个有符号类型完整表示(显然99作为unsigned int,完全在long的取值范围内)。
- 所以
a(99,unsigned int)会被转换为99L(有符号long),然后比较99L > -2L,结果显然是true,返回1。
为什么foo的汇编直接返回0x1?
编译器在这里做了常量折叠/死代码消除的优化:因为任何unsigned int的值转换为long后都是非负数(unsigned类型的值本身就不会是负的,且long能容纳所有unsigned int的取值),而非负数必然大于-2L。也就是说,不管你给foo传什么unsigned int参数,a > -2L的结果永远是true,所以编译器直接把整个函数优化成了return 1;,对应汇编就是mov eax, 0x1。
内容的提问来源于stack exchange,提问作者Sourav Ganguly
相关产品推荐
相关产品推荐

