而今,重走C++路——基础(2)

类型转换

当在程序的某处我们使用了一种类型而其实对象应该取另一个类型时,程序会自动进行类型转换。

bool b = 42; // b为真
int i = b; // i的值为1
i = 3.14; // i的值为3
double pi = i; // pi的值为3.0
unsigned char c = -1; // 假设char占8比特,c的值为255
signed char c2 = 256; // 假设char占8比特,c2的值是未定义的
  • 非布尔类型的算数值赋给布尔类型时,初始值为0,结果为False,否则结果为True;
  • 当把一个布尔值赋给非布尔类型时,初始值为False,结果为0,初始值为True,结果为1;
  • 把一个浮点数赋值给整数类型时,进行了近似处理。结果值仅保留浮点数中小数点前面的部分;
  • 把一个整数赋值给浮点类型时,小数部分记为0。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失;
  • 赋值给无符号类型一个超出它所表示的值时,结果是初始值对无符号类型表示数值总数去模后的余数。1
    例如,8比特大小的unsigned char可以表示0至255区间内的值,如果赋了一个区间外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255;
  • 当赋值给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
避免无法预知和依赖于实现环境的行为
1. 无法预知的行为源于编译器无须(有时是不能)检测的错误。即使代码编译通过了,如果程序执行了一条未定义的表达式,仍有可能产生错误。
2. 程序也应该尽量避免依赖于实现环境的行为。例如:把int的尺寸看成是一个确定不变的已知值,那么这样的程序就称作不可移植的(nonportable)

含有无符号类型的表达式

unsigned u = 10;
int i = -42;
std::cout << i + 1 << std::endl; // 输出-84
std::cout << u + 1 << std::endl; // 如果int占32为,输出4294967264

在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出的表达式里,相加前首先把整数-42转换成无符号数,类似于直接给无符号数赋一个值,结果等于这个负数加上无符号数的模

当从无符号中数中减去一个值时,不管这个值是不是无符号数,我们必须确保结果不可能是一个负数:

unsigned u1 = 42, u2 = 10;
std::cout << u1 - u2 << std::endl; // 正确:输出32
std::cont << u2 - u1 << std::endl; // 正确:不过,结果是取模后的值

无符号数不会小于0——这关系到循环的写法。

// 正确,通过控制变量递减的方式把从10到0的数字降序输出。
for (int i = 0; i >= 0; --i){
	std::cout << i << std::endl;
}

// 错误:变量u永远也不会小于0,循环条件一直成立。
for (unsigned u =10; u = 10; --u){
	std::cout << u << std::endl;
}
/* 
 * 异常分析:结果为-1时,并不满足无符号的要求,这是-1会被自动转换为一个合法的无符号数。
 * 假设int类型占32位,则当u等于0时,--u的结果将会是4294967295.
 */

解决方法

// 一种解决办法时,使用while语句来代替for语句,因为前者让我们能够在输出变量之前(而非之后)先减去1:
unsigned u = 11; // 确定要输出的最大数,从比它大1的数开始。
while (u > 0){
	--u; // 先减1,这样最后一次迭代的时候就会输出0;
	std::out << u << std::endl;
}

Exercise

#include "iostream"
using namespace std;

int main() {
	unsigned u = 10, u2 = 42;
	std::cout << u2 - u << std::endl; // 32
	std::cout << u - u2 << std::endl; // 4294967264
	int i = 10, i2 = 42;
	std::cout << i2 - i << std::endl; // 32
	std::cout << i - i2 << std::endl; // -32
	std::cout << i2 - u << std::endl; // 32
	std::cout << u - i << std::endl; // 0
	cin.get();
	return 0;
}

  1. 例如:(-1+256) % 256. 负数比较特殊,需要加上被除数的整数倍,然后算出来大于0的数,然后再模运算(详情) ↩︎

你可能感兴趣的:(C++)