第三章 变量和数据类型_C语言整数的取值范围以及数值溢出

short、int、long 是C语言中常用的三种整数类型,分别称为短整型、整型、长整型。
在现代操作系统中,short、int、long 的长度分别是 2、4、4 或者 8,它们只能存储有限的数值,当数值过大或者过小时,超出的部分会被直接截掉,数值就不能正确存储了,我们将这种现象称为溢出(Overflow)。
溢出的简单理解就是,向木桶里面倒入了过量的水,木桶盛不了了,水就流出来了。
要想知道数值什么时候溢出,就得先知道各种整数类型的取值范围。

无符号数的取值范围

计算无符号数(unsigned 类型)的取值范围(或者说最大值和最小值)很容易,将内存中的所有位(Bit)都置为 1 就是最大值,都置为 0 就是最小值。

以 unsigned char 类型为例,它的长度是 1,占用 8 位的内存,所有位都置为 1 时,它的值为 28 - 1 = 255,所有位都置为 0 时,它的值很显然为 0。由此可得,unsigned char 类型的取值范围是 0~255。

前面我们讲到, char 是一个字符类型,是用来存放字符的,但是它同时也是一个整数类型,也可以用来存放整数,请大家暂时先记住这一点,更多细节我们将在《 在C语言中使用英文字符》一节中介绍。

有读者可能会对 unsigned char 的最大值有疑问,究竟是怎么计算出来的呢?下面我就讲解一下这个小技巧。

将 unsigned char 的所有位都置为 1,它在内存中的表示形式为1111 1111,最直接的计算方法就是:

20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 = 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255

按照这种巧妙的方法,我们可以很容易地计算出所有无符号数的取值范围(括号内为假设的长度):

unsigned char unsigned short unsigned int(4字节) unsigned long(8字节)
最小值 0 0 0 0
最大值 2^8 - 1 = 255 2^16 - 1 = 65,535 ≈ 6.5万 2^32 - 1 = 4,294,967,295 ≈ 42亿 2^64 - 1 ≈ 1.84×1019

有符号数的取值范围

按照上面的方法,我们可以计算出所有有符号数的取值范围(括号内为假设的长度):

char short int(4个字节) long(8个字节)
最小值 -2^7 = -128 -2^15 = -32,768 ≈ -3.2万 -2^31 = -2,147,483,648 ≈ -21亿 -2^63 ≈ -9.22×1018
最大值 2^7 - 1= 127 2^15 - 1 = 32,767 ≈ 3.2万 2^31 - 1 = 2,147,483,647 ≈ 21亿 2^63 - 1≈ 9.22×1018

char、short、int、long 的长度是有限的,当数值过大或者过小时,有限的几个字节就不能表示了,就会发生溢出。发生溢出时,输出结果往往会变得奇怪,请看下面的代码:

第三章 变量和数据类型_C语言整数的取值范围以及数值溢出_第1张图片

运行结果:
a=0, b=-1

变量 a 为 unsigned int 类型,长度为 4 个字节,能表示的最大值为 0xFFFFFFFF,而 0x100000000 = 0xFFFFFFFF + 1,占用33位,已超出 a 所能表示的最大值,所以发生了溢出,导致最高位的 1 被截去,剩下的 32 位都是0。也就是说,a 被存储到内存后就变成了 0,printf 从内存中读取到的也是 0。

变量 b 是 int 类型的有符号数,在内存中以补码的形式存储。0xffffffff 的数值位的原码为 1111 1111 …… 1111 1111,共 32 位,而 int 类型的数值位只有 31 位,所以最高位的 1 会覆盖符号位,数值位只留下 31 个 1,所以 b 的原码为:

1111 1111 …… 1111 1111

这也是 b 在内存中的存储形式。

当 printf 读取到 b 时,由于最高位是 1,所以会被判定为负数,要从补码转换为原码:

第三章 变量和数据类型_C语言整数的取值范围以及数值溢出_第2张图片

 最终 b 的输出结果为 -1。

 

你可能感兴趣的:(C语言重难点,c语言,开发语言)