先看下面代码:
void main()
{
unsigned int i=8;
if(i>=-1) printf("8>=-1\n");
else printf("8<-1");
}
输出结果是8<-1,明明赋值为8的变量i,结果被程序判定比-1还小,这是怎么回事?
问题根源在于变量i定义中的unsigned。我们都知道,int/short/char等类型分signed和unsigned。C的表达式中signed和unsigned混合运算有三种情况:a.操作数全为signed;b.操作数全为unsigned;c.操作数混合了signed和unsigned。前两种情况,相同符号操作没什么问题,可情形c.涉及不同符号间的混合计算就要注意,编译器会自动对操作数进行规整化:只要表达式中存在一个无符号数,所有操作数都被转化为无符号数,运算按相应无符号操作符进行,计算结果也是一个无符号数。
因此作为混合符号运算,上例if(i>=-1)中,无符号的i使原本默认有符号的-1被转换成无符号的大整数,判断条件就不成立。类似情况很多,例如下面常见的笔试题:
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这也是考查应试者是否了解符号自动规整原则:a为无符号数,a+b时编译器把b也转换成一个无符号整数,这样a+b的结果大于6,而不是想当然的-14。以上两个例子还有迹可寻,可下面这个就:
void main()
{
int x = 2;
char * str = "abcd";
int y = (x - strlen(str) ) / 2;
printf("%d\n",y);
}
结果是-1吧?No,实际输出2147483647。因为大家容易忽略一点,strlen返回值类型size_t是unsigned int的重定义(有人注意过么?),这样strlen的无符号返回值进入表达式,使原本有符号的x,y都被自动转换成无符号。
结论
隐含的符号自动转换是一大陷阱。特别是数值运算类程序中,一旦混用有符号/无符号数,甚至说只要使用了unsigned及其重定义类型,都有可能发生错误,要加倍小心!
为防止意外转换,一方面要显式指定类型,不要用int,char这种缺少提示的中性表示法,这种含糊的表示用多了,会本能地忽略和回避符号问题;另一方面要谨慎选用unsigned型,不要仅仅因为无符号数没有负值就用它表示数量,比如有人喜欢用unsigned int定义for/while循环计数量,这很不安全,一不小心在循环内和负数做比较,就会发生逻辑错误或死循环;最后,如果非要让unsigned型参与计算,可以用强制类型转换保证中间操作数和结果为signed。