隐式类型转换和负数的补码
转载自:http://www.cppblog.com/suiaiguo/archive/2009/07/16/90228.html
偶然看到一道C++面试题:
void foo(void) { unsigned int a = 6; int b = -20; (a+b>6)?puts(">6"):puts("<=6");//puts为打印函数 }
问输出是什么?答案是输出 >6。 这道题主要考察两个东西。 1.隐式类型转换:int型变量转化成unsigned int, b成了正数. 2.负数的补码:计算机系统中的数值是以补码形式表示(存储)的。
一、C++隐式类型转换
C++定义了一组内置的类型对象之间的标准转换,在必要时它们被编译器隐式的应用到对象上。在算式转换保证了二元操作符,如加法或乘法的两个操作数被提升为共同的类型,然后再用它表示结果的类型。两个通用的指导原则如下:
1、为防止精度损失,如果必要的话,类型总是被提升为较宽的类型。
2、所有含有小于整形的有序类型的算术表达式在计算之前其类型都会被转换成整形。 规则的定义如上面所述,这些规则定义了一个类型转换层次结构,我们从最宽的类型long double 开始,那么另一个操作数无论是什么类型都将被转换成long double .如果两个操作数千不是long double 型,那么若其中一个操作数的类型是double 型,则另一个就被转换成double 型。例如:
int ival; float fval; double dval; dval + fval + ival //在计算加法前fval和ival都被转换成double
类似地,如果两个操作数都不是double型而其中一个操作float型 ,则另一个被转换成float型。例如:
char cval; int ival; float fval; cval + ival + fval //在计算加法前ival和cval都被转换成float
否则如果两个操作数都不是3种浮点类型之一,它们一定是某种整值类型。在确定共同的目标提升类型之前,编译器将在所有小于int
的整值类型上施加一个被称为整值提升的过程。
在进行整值提升时类型char、signed char、unsigned char和short int
都被提升为类型int 。如果机器上的类型空间足够表示所有unsigned short 型的值,这通常发生在short用半个字而int
用一个字表示的情况下,则unsigned short int 也被转换成int 否则它会被提升为unsigned int
。wchar_t和枚举类型被提升为能够表示其底层类型所有值的最小整数类型。在下列表达式中:
char cval; bool found; enum mumber{m1,m2,m3}mval; unsigned long ulong; cval + ulong;ulong + found; mval + ulong;
在确定两个操作数被提升的公共类型之前,cval found 和mval都被提升为int 类型。
一旦整值提升执行完毕,类型比较就又一次开始。如果一个操作数是unsigned long 型,则第二个也被转换成unsigned long
型。在上面的例子中所有被加到ulong上的3个对象都被提升为unsigned long 型。如果两个操作数类型都不是unsigned long型
而其中一个操作数是long型,则另一个也被转换成long型。例如:
char cval; long lval; cval + 1024 + lval; //在计算加法前cval和1024都被提升为long型。
long类型的一般转换有一个例外。如果一个操作数是long型而另一个是unsigned int 型,那么只有机器上的long型的长度足以容纳unsigned
int 的所有值时(一般来说,在32位操作系统中long型和int 型都用一长表示,所以不满足这里的假设条件),unsigned int
才会被转换为long型,否则两个操作数都被提升为unsigned long 型。若两个操作数都不是long型而其中一个是unsigned int
型,则另一个也被转换成unsigned int 型,否则两个操作数一定都是int 型。
一般来说各种类型的长度关系为 long double > double > float >= int
>= short > char,unsigned > signed 。
尽管算术转换的这些规则带给你的困惑可能多于启发,但是一般的思想是尽可能地保留类型表达式中涉及到的值的精度。这下是通过把不同的类型提升到当前出现的最宽的类型实现的。
二、负数的补码
在计算机系统中,数值一律用补码来表示(存储)。
主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
补码与原码的转换过程几乎是相同的。
数值的补码表示也分两种情况:
(1)正数的补码:与原码相同。
例如,+9的补码是00001001。
(2)负数的补码:符号位为1,其余位为该数绝对值的原码按位取反;然后整个数加1。
例如,-7的补码:因为是负数,则符号位为“1”,整个为10000111;其余7位为-7的绝对值+7的原码0000111按位取反为1111000;再加1,所以-7的补码是11111001。
已知一个数的补码,求原码的操作分两种情况:
(1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。
(2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1。
例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为“1”;其余7位1111001取反后为0000110;再加1,所以是10000111。