与本文有关的可以结合这篇文章一起看:C语言不踩坑: 自动类型转换规则 ,这篇文章讲的是在一个运算式中出现不同类型数据的情况
1.首先看一个例程:
#include <stdio.h>
int main(void)
{
int a = -1;
unsigned int b = 1;
if (a > b) {
printf("a > b\n");
} else {
printf("a <= b\n");
}
return 0;
}
VS运行结果:
VS在编译时还给出了警告:
很明显 -1>1 的结果令人意外,这是因为C语言在两个不同类型变量进行比较时,进行了自动类型转换,这里的int类型的a在与unsigned int的b比较时,a自动转变成了unsigned int,也就是4294967295,所以实际是4294967295与1在比较,结果自然是4294967295大于1。
2.再来看一种现实在写程序时更容易遇到的情况
sum_elements是一个求数组中前length个数据的和的函数,数组中元素的个数由参数length给出
int sum_elements(int a[], unsigned int length)
{
int i;
int result = 0;
for (i = 0; i <= length - 1; i++)
result += a[i];
return result;
}
如果我告诉你这是一段有错的代码,可能你也不太相信,因为这个函数的一切看起来是这么的自然,因为数据的长度(或个数)肯定是一个非负数,所以把length声明为一个unsigned int很合理,计算的数据个数和返回类型也正确。的确如此,但是这都是在length不为0的情况,试想,当调用函数时,把0作为参数传递给length会发生什么事情?
int main(void)
{
int a[] = {1, 2, 3};
int m = sum_elements(a, 0); // 故意传 0
printf("%d\n", m);
return 0;
}
回想一下前面我们所说的知识,因为length是unsigned类型,所以所有的运算都被隐式地被强制转换为unsigned类型,所以length-1(即0-1 = -1),-1对应的无符号类型的值为UMax,所以for循环将会循环UMax次,数组也会越界,发生错误。
如果想改进上面的程序:
for(i = 0; i < length; ++i)
这样当length为0也不会有问题。
3.判断第一个字符串是否长于第二个字符串,若是,返回1,若否返回0,代码如下:
int strlonger(char *s1, char *s2)
{
return strlen(s1) - strlen(s2) > 0;
}
注意这里有一个数据类型size_t,它被定义在stdio.h文件中,其实它就是unsigned int,一个字符串的长度当然不可能为负,这样的定义显然是合理的,但是有时却因为这样,而存在不少的问题,如函数strlonger的实现。当s1的长度大于等于s2时,这个函数并没有什么问题,但是你可以想像,当s1的长度小于s2的长度时,这个函数会返回什么吗?没错,因为此时strlen(s1) - strlen(s2)为负(从数学的角度来解释的话),而又由于程序把它作为unsigned为处理,则此时的值肯定是一个比0大的值。换句话来说,这个函数只有在strlen(s1) == strlen(s2)时返回假,其他情况都返回真。
如果想改进上面的程序:
int strlonger(char *s1, char *s2)
{
return strlen(s1) > strlen(s2);
}
这样就可以利用两个无符号数进行直接的比较,而不会因为减法而出现负数而影响比较结果。
总结:
1.不要把有符号和无符号数进行比较
2.当比较符号的一边有减法运算式时注意有无可能运算结果为负数
3.注意无符号的使用,因为在unsigned的世界里,x-y>0与x>y是不等价的