为了更好地解释下面的代码,先来介绍一些背景知识,在我的计算机中, char 类型占 8 个比特位,那么, unsigned char 类型能表示的数的范围为 0 ~ 2的8次方 – 1,即 0 ~ 255,共 256 个数;int 类型占 32 个比特位,那么 unsigned 类型所能表示的数的范围为 0 ~ 2的32次方 – 1,即 0 ~ 4294967295,共 4294967296 个数,接下来看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
int main()
{
unsigned char c = -10;
unsigned char j = 258;
int d = c;
std::cout << "d = " << d << std::endl;
int k = j;
std::cout << "k = " << k << std:: endl;

int i = -42;
unsigned u = 10;
std::cout << "i + u = " << i + u
<< std:: endl;

return 0;
}

这是我写在 Emacs 上的代码,运行结果如下图:

下面对上图的运行结果进行解释:

unsigned char 类型能表示的数的范围为 0 ~ 255,但是我们给属于此类型的 c 和 j 赋值分别为 -10 和 258,显然超过了此类型所能表示的数的范围,在此例中,有以下三种情况:

( 一 )、 当我们所赋的值为 负值 时,如以上代码中变量 c 的情形,给它一个值 -10,最后将 c 的值赋给 d 后输出,得到 d = 246,即给 c 变量赋值 -10 后,它的值变为 246,这是为什么呢?这是由于当我们给一个无符号类型赋一个 负值 时,其结果是我们所赋的值与这个无符号类型能表示的数的总个数的和,即 d = -10 + 256,这样一来结果当然是 246 了,显然 246 是在此无符号类型所能表示的数的范围内的(0 ~ 255),那么,如果相加后倘若仍然不在这个无符号类型所能表示的数的范围内该怎么办呢?譬如以下的代码:

1
2
3
4
5
6
7
8
9
#include <iostream>
int main()
{
unsigned char m = -258;
int n = m;
std::cout << "n = " << n << std::endl;

return 0;
}

运行后的结果如下:

给 m 赋值为 -258 ,最后 n = 254 ,即 m 的值变为了254 ,这又是什么情况呢?按照以上分析的逻辑, -258 + 256 = -2, -2 仍然不在 unsigned char 所能表示的数的范围内,这个时候怎么办呢?显然,只需要将 -2 + 256,等于 254, 254 在 unsigned char 所能表示的数的范围内,所以最后的结果为 254,跟以上运行最后得出的结果相符合,故由以上分析,可以得出以下结论:

当我们给一个无符号类型赋一个超过其表示范围的负值时,其最后的结果是该负值与该无符号类型所能表示的数的总个数的和,如果所得结果还是一个不在此类型表示范围的负数,则将所得结果重复以上相加的过程,直到最后得到一个在其表示范围的数,此即为最后的结果

( 二 )、 让我们把目光转回到第一个代码片段,将 unsigned char j 赋一个超出其表示范围内的 正值 258 后,将 j 的值赋给 k,最后输出 k = 2,即 j 的值变为了 2 ,这又是为什么呢?实际上,当我们赋给一个无符号类型一个超出它表示范围的 正值 时,结果是将我们所赋的这个值对此无符号类型所能表示的数的总个数取模后的余数,即 258 % 256 = 2,符合程序运行结果

( 三 )、 观察第一个代码片段中第 11 行之后的部分,一个有符号数 i 与一个无符号数 u 相加的情形,最后得到 4294967264 这么一个奇怪的数字,而不是像希望的一样得到 -32 ,这是为什么呢?这是因为 这个表达式中无符号数大于有符号数 , 此种情形下, 当把一个有符号类型和无符号类型相加时,需要先将有符号类型的数转换为无符号类型的数后再进行加法运算 ,(一)(二)中已经详细说明了怎样将一个有符号类型的数转换为一个无符号类型的数,对于这种情况,-42 + 4294967296 = 4294967254 ,在 unsigned 的表示范围内,即 -42 转换为无符号类型后为 4294967254 ,然后再将此数值加上 i (即 10 ) ,得 4294967264 ,符合最后得到的程序运行结果

最后我们还需要另外强调的一点是, 当我们给带符号类型赋予一个超过其表示范围的值时,其结果是未定义的 ,程序可能继续执行,可能崩溃,也可能生成垃圾数据。