带符号和无符号类型
除去布尔型和扩展的字符型之外,其他整型可以划分为带符号的和无符号的两种。带符号类型可以表示正数、负数和0,无符号类型则仅能表示大于等于0的值。
类型int、short、long和long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,例如unsigned long。类型unsigned int可以编写为unsigned。
与其他整型不同,字符型被分为了三种:char、signed char和unsigned char。特别需要注意的是:类型char和类型signed char并不一样。尽管字符型有三种,但是字符的表现形式却只有两种:带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体是哪种由编译器来决定。
如何选择类型
和C语言一样,C++的设计准则之一也是尽可能地接近硬件。C++的算术类型必要满足各种硬件特质,所以它们常常显得繁杂而令人不知所措。事实上,大多数程序员能够对数据类型的使用做出限定从而简化选择的过程。以下是一些经验准则:
- 当明确知道数值不可能为负时,选用无符号类型。
- 使用int执行整数运算。在实际应用中,short常常显得太小而long一般和int有一样的尺寸。如果你的数值超过了int的表示范围,选用long long。
- 在算术表达式中不要还有char或bool,只有在存放字符或布尔值时才使用它们。因为类型char在一些机器上是有符号的,而在另一些机器上又是无符号的,所以如果使用char进行运算特别容易出问题。如果你需要使用一个不大的整数,那么明确指定它的类型是signed char或者unsigned char。
- 执行浮点数运算选用double,这是因为float通常精度不够用而双精度浮点数和单精度浮点数的计算代价相差无几。事实上,对于某些机器来说,双精度运算甚至比单精度还快。long double提供的精度在一般情况下是没有必要的,况且它带来的运行时消耗也不容忽视。
类型的转换
类型所能表示的值的范围决定了转换的过程:
- 当我们把一个非布尔型的算术值赋给布尔类型时,初始值0则结果是false,否则结果为true。例如:bool b=42;
- 当我们把一个布尔型值赋给一个非布尔型时,初始值为false则结果是0,初始值为true则结果为1.
- 当我们把一个浮点数赋给整型类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
- 当我们把一个整数值赋给浮点类型时,小数部分记为0。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
- 当我们赋给无符号类型一个超过它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,将-1赋给8比特的unsigned char 所得的结果是255.
- 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
含有无符号类型的表达式
尽管我们不会故意给无符号对象赋一个负值,却可能写出这么做的代码。
例如,当一个算术表达式中既有无符号数又有int值时,那个int值就会转换为无符号数。把int转换为无符号数的过程和把int直接覆给无符号变量一样:
unsigned u=10;
int i=-42;
cout<<i+i<<endl; //输出-84
cout<<u+i<<endl; //如果int占32位,输出4294967264
在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换为无符号数。把负数转换为无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不过这个值是不是无符号的数,我们都必须确保结果不能是一个负值。
如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动转换为无符号数。例如,在一个形如”a*b"的式子中,如果a=-1,b=1,而且a是int,b是unsigned,则结果须视在当前机器上int所占位数而定。