C++基本数据类型备忘

今天阅读《C++ Primer, 5e》的第二章,介绍C++的基本内置类型,觉得有一些平时工作容易出错的知识点,现摘录如下:


unsigned char c = -1; // 假设char占8比特,c的值为255

当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255。


signed char c2 = 256; // 假设char占8比特,c2的值是未定义的

当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。


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

在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负数,结果等于这个负数加上无符号数的模。unsigned (int)的取值范围是0~2^{32}-1,所以总数有2^{32}个数,-42%2^{32}=-42+2^{32},u+i=10+(-42+2^{32})=4294967264。


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

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


无符号数不会小于0这一事实同样关系到循环的写法。例如我们常用的循环如下:

for (int i = 10; i >= 0; --i)
    std::cout << i << std::endl;

可能你会觉得反正也不打算输出负数,可以用无符号数来重写这个循环。然而,这个不经意的改变却意味着死循环;

// 错误:变量u永远也不会小于0,循环条件一直成立
for (unsigned u = 10; u >= 0; --u)
    std::cout << u << std::endl;

来看看当u等于0时发生了什么,这次迭代输出0,然后继续执行for语句里的表达式。表达式--u从u当中减去1,得到的结果-1并不满足无符号数的要求,此时像所有表示范围之外的其他数字一样,-1被自动地转换成一个合法的无符号数。假设int类型占32位,则当u等于0时,--u的结果将会是-1%2^{32}=4294967295。

一种解决的办法是,用while语句来代替for语句,因为前者让我们能够在输出变量之前(而非之后)先减1:

unsigned u = 11; // 确定要输出的最大数,从比它大1的数开始
while (u > 0){
    --u;
    std::cout << u << std::endl;
}

Leave a Reply

Your email address will not be published. Required fields are marked *