无符号数与有符号数之间存在着很多细节问题,稍有不慎就可能导致程序出现不可预料的错误。如果说是在长度相同的数值类型之间相互转化或者向长度比较短的数据转化是没有问题的,结果都是已定义的:多余的位将被简单的丢弃。但是,一旦需要向长度更长的数据类型转化这个问题就会变得十分蹊跷。例如编译器在转化char类型到int类型时,需要作出选择:应该讲字符作为有符号数还是应该无符号数处理?如果是前一种情形,编译器在将char类型扩展到int类型时,应该同时复制符号位;而如果是后一种情况,编译器只需要在多余的位上直接填充0即可。
下面是自己总结的一个小陷阱,如果将一个较短的数值转化为一个较长的数值的时候,尤其是转化为无符号数的情形下,问题就会出现。因为数值会首先转化为较长类型的有符号数,然后再同类型转化为无符号数。例如:c是一个字符变量,使用(unsigned)c就可以得到与c等价的无符号整数,这是失败的。因为在将字符c转化为无符号整数时,c将首先被转化为int型整数,而此时可能得到非预期的结果。
正确的方式是使用(unsigned char)c,因为一个unsigned char类型的字符在转化为无符号整数时无需首先转化为int型整数,而是直接进行转化。
下面是我在Visual Studio 2008下测试的结果。
#include <stdio.h>
int main()
{
char a = -2;
unsigned char b = (unsigned char)a;
int c = a;
unsigned int d = (unsigned int) a;
unsigned int e = (unsigned char)a;
printf("a = %d\nb = %d\nc = %d\nd = %d\ne = %d\n",a,b,c,d,e);
return 0;
}
上述代码执行结果为:
可以发现VS2008的编译器数据转化时是将符号位扩展出去的。注意unsigned int d = (unsigned int )a语句,将a首先转化为int,此时为-2,将-2转化为unsigned int其实没有起作用,打印的时候仍然是按照有符号数打印的。
总结:在执行短数据向高数据转化时,首先在本类型上完成无符号转化,然后向高数据转化。这样做的目的是多余位直接补0.
发现一个问题:
#include <stdio.h>
int main()
{
short a = -2;
unsigned int d = (unsigned short) a;
printf("a = %d\nd = %d\n",a,d);
return 0;
}
执行结果为:
如果同类型之间的转化则出现了问题
#include <stdio.h>
int main()
{
int a = -2;
unsigned int d = (unsigned int) a;
printf("a = %d\nd = %d\n",a,d);
return 0;
}
执行结果为:
猜测:这就跟printf跟的格式化参数有关,使用%d则认为最高位为符号位,打印出来的结果按照有符号数打印,如果想打印出无符号数应该使用格式化参数%u.
#include <stdio.h>
int main()
{
unsigned int a = 0Xf0000001;
unsigned int d = (unsigned int) a;
printf("a = %d\nd = %d\n",a,d);
return 0;
}
执行结果为: