class="markdown_views prism-atom-one-light">
1.有符号数和无符号数间的比较
cout<<( 1 > -2) < < endl; // 1 正常,都是有符号数
cout < <((unsigned int)1>-2) < < endl; // 0 -2被转换为无符号数.
cout < <((unsigned int)1>-2.) < < endl; // 1 float不存在无符号数,所以,无符号数肯定大于float型的负数!
在virual c++6中
这个问题测试是否懂得C语言中的整数自动转换原则,有些开发者懂得极少这些东西。当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此,从这个意义上讲,无符号数的运算优先级要高于有符号数,这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
首先进行一个实验,分别定义一个signed int型数据和unsigned int型数据,然后进行大小比较:
unsigned int a=20;
signed int b=-130;
a>b?还是b>a?实验证明b>a,也就是说-130>20,为什么会出现这样的结果呢?
这是因为在C语言操作中,如果遇到无符号数与有符号数之间的操作,编译器会自动转化为无符号数来进行处理,因此a=20,b=4294967166,这样比较下去当然b >a了。
再举一个例子:
unsigned int a=20;
signed int b=-130;
std::cout < < a+b < < std::endl;
结果输出为4294967186,同样的道理,在运算之前,a=20,b被转化为4294967166,所以a+b=4294967186
减法和乘法的运算结果类似。
如果作为signed int型数据的b=-130,b与立即数之间操作时不影响b的类型,运算结果仍然为signed int型:
signed int b=-130;
std::cout < < b+30 < < std::endl;
输出为-100。
而对于浮点数来说,浮点数(float,double)实际上都是有符号数,unsigned 和signed前缀不能加在float和double之上,当然就不存在有符号数根无符号数之间转化的问题了。
2.无符号数与有符号数比较的几个题目
题目一:
int a = -1;
unsigned int b = 1;
printf(“%d”, a > b);
结果输出:1
因为无符号数与有符号数比较时,要将有符号数转化为无符号数,再来比较。a转化为无符号数后就是0xFFFFFFFF,肯定大于b
题目二:
char a = -1;
unsigned char b = 1;
printf(“%d”, a > b);
结果输出:0
奇怪了,怎么会这样?这是因为两者被转化为int了,a转化为int就表示-1,b转化为int 就是1,前者小于后者。注意了这里不是像题目一里一样简单的把char转化为unsigned char了
题目三:
int a = -1;
unsigned char b = -1;
printf(“%d
”, a < b);
结果输出是:1
原因在于要把b转化为int就是0xFF当然大于-1了
题目四:
char a = -1;
unsigned int b = -1;
printf(“%d
”, a == b);
结果输出:1
原因是char类型被扩展为unsigned int后与b相等,同为0xFFFFFFFF
一些结论:
无符号int与有符号int比较大小,转化为无符号int来比较;
int类型与非无符号int的类型比较时,非无符号int的类型转化为int来比较;
无符号int类型与其他类型如unsigned short,signed short,unsigned char, char 比较时,其他类型一律转化为无符号int类型来比较;
非无符号int类型和非int类型如unsigned short,signed short,unsigned char, char 比较时,一律转化为int类型来比较;
3.有符号数与无符号数的除法
有这样一个问题:
int a = -2; printf(“%d,%d”, a/4*4, a/4U*4);
打印出的是:0,-4
有符号数与无符号数做除法时,双方都转为无符号数再做除法;
-2在计算机中的存储形式为:0xff ff ff fe
作为无符号数除以4时,向右移2位(左边补零),中间结果为0x3f ff ff ff
之后乘以4时,向左移位(右边补零),结果为oxff ff ff fc
该数字表示有符号数-4,于是-4就被打印出来。
有关C语言中有符号/无符号数混合运算的小问题
有符号数与无符号数混合运算可能有三种混合方式:
1.操作数全为有符号数如:
int a = -1;
int b = 2;
a + b;
2.操作数全为无符号数如:
unsigned int a = (unsigned int)-1;
unsigned int b = 2;
a + b;
3.操作数混合了有符号数,无符号数如:
int a = -1;
unsigned int b = 2;
a / b;
情形1和2就不用说,因为运算中涉及的都是相同符号特征的操作数,计算过程中不会引起歧义。
而对于情形3,由于涉及到了符号特性相异的操作数,情况就有些复杂了。
这里先说一下,对于有些运算操作,是要区分有符号与无符号的情况的。比如有符号的除法与无符号的除法,有符号的取模运算与无符号的取模运算,其计算语意是不同的,具体来说,有符号的除法在x86平台上对应的汇编指令是idiv,而无符号的除法对应的则是div。
而对于另外一些操作运算,则是不区分有符号与无符号的,比如,加法,减法,乘法运算。
对于不同操作符与符号相关的情形可以通过下面的小程序来验证:
int main()
{
signed int n1 = 1;
signed int n2 = 2;
unsigned int u1 = 1;
unsigned int u2 = 2;
signed int a = n1 + n2;
unsigned int ua = u1 + u2;
signed int s = n1 - n2;
unsigned int us = u1 - u2;
signed int m = n1 * n2;
unsigned int um = u1 * u2;
signed int d = n1 / n2;
unsigned int d = u1 / u2;
signed int m = n1 % n2;
unsigned int um = u1 % u2;
return 0;
}
对这一段程序调用 g++ -S,生成相应的汇编文件,就会发现:
加法运算,无论是有符号还是无符号,对应的都是addl指令,
减法运算,对应的都是subl指令,
乘法运算,对应的都是imul指令,
有符号除法运算对应的是idiv指令,无符号除法对应的则是 div指令,
有符号数取模运算会用到idiv指令,无符号取模用的则是div指令。
所以回到初始的问题,情形1和2的行为是容易预期的,因为所有操作数都具有同样的符号特性,直接就可以得出采用相应符号特性的运算类型。
对于情形3,因为涉及到不同符号数的混合计算,在计算之前需要先对操作数进行规整化的动作,规整的原则就是如果操作数中存在至少一个无符号数,则所有操作数都被转化为无符号数,运算操作也采用相应的无符号操作符进行,计算完的结果也是一个无符号数。
举例来说:
(unsigned int)a / (signed int)b 会采用无符号除法进行,其实质相当于
(unsigned int)a / (unsigned int)b
计算结果也是一个无符号数。
再进一步,对于运算-2 / -1,如果采用有符号数运算,结果是2,采用无符号数运算,结果则是0。
所以 (signed int)(-2) / (unsigned int)(-1)的结果就是0了。
除法,取模这样的操作符在不同的上下文语境里对应的语义动作也有所不同,而且这种差异还不同于c++里的操作符重载在语言级别可见,而是要到更底层的汇编语言级别才可见,这多少就有一些tricky,也容易诱使程序员犯错了。