2.3 int类型——总结与补充
2.3.1 计算机表示负整数的几种方法
前面代码中涉及到的数据仅仅是int类型的正整数。本小节讨论有关负整数的有关问题。
由于在计算机内部只能存储、处理0、1这样的数字,所以正负号也只能用0、1表示。通常用0表示正,用1表示负。
到目前为止,计算机中曾有过3种负整数的表示方法。
1.原码表示法
原码表示法用机器数中的某一位表示正负号(通常是最高位),其余各位(通常是低位)用来记录数的绝对值。
这样,如果用16bits来记录一个数据的话,那么最多只能有15bits来记录数的绝对值,还有一位要用来记录正负号,假定记录正负号的位是第一位的话,那么+123和-123在机器里的表示是:
0000 0000 0111 1011和1000 0000 0111 1011
用这种表示方法得到的机器数叫做原码。16bits的原码可以表示从1111 1111 1111 1111到0111 1111 1111 1111(-32767D到+32767D)之间的所有整数。
原码中1000 0000 0000 0000和0000 0000 0000 0000表示的是同一个数,叫做-0和+0。
2.反码表示法
另外一种叫做反码的表示方法只在表示负数时和原码有所不同。负数的反码是把负数的原码的绝对值部分的0改写成1、1改写成0,并且依然用第一位为0或1表示正负号。这样+123和-123在机器里如果用反码表示的话就是:
0000 0000 0111 1011和1111 1111 1000 0100
反码同样能表示从1000 0000 0000 0000到0111 1111 1111 1111(-32767D到+32767D)之间的所有整数。而且在反码中也有一个+0和一个-0。
3.补码表示法
除了以上两种数的表示方法之外,计算机中最常用的是补码表示法。
正数的补码表示方法和前面两种表示方法是一致的,负数的补码一般则是反码加上个1的结果。此外原码的-0被补码用来表示-32768。这样,+123和-123的补码格式的机器数就是:
0000 0000 0111 1011和1111 1111 1000 0101
4.怎样求补码
求一个负数的补码首先要知道用来表示这个机器数的位数,之后可以用上面所说的方法,先求原码、再求反码,最后就可以得到补码了。这里再次强调一下,对于正数三者的表示是完全一致的,没那么多复杂的手续。
求一个负数的补码,还可以用2机器数的总位数减去这个数直接得到。比如对于-123,如果机器数的长度为16位,216-123 = 65413 D = 1111 1111 1000 0101B。实际上这就是补码的定义。顺便说一下,在原理上,补码和时钟表示时间极其类似,0点的前三小时(-3)实际上就是9点(12-3)。
2.3.2 计算机码制和C语言的关系
机器数釆用哪种码制是CPU的事情,C语言管不到这里。而且不管计算机使用的是哪种码制,都可以应用c语言来编程。但是由于最常见的、最通用的大多数计算机釆用的是补码,所以后面一律用补码来进行说明。但同时应该清楚的是,C语言并非仅限于应用在使用补码的计算机上。
尽管C语言不是单纯面向补码体制的,但由于本书是针对并通过这种最常见的情况来讲解C语言的,因此熟悉如何写补码是阅读本书后面内容的基础,甚至是基础的基础。
练习
当使用16bits表示一个整数时,写出12345D和-12345D的补码形式。
2.3.3 暂时不必关心的一些细节
还有几个问题顺便交代一下。
首先,在前面的整数的表示模型的说明中,忽略了某些位不使用的情形(14)。这种令人头疼的遭遇很罕见,但的确有。
其次,前面只说明了整数的表示模型而没有说明小数是如何表示的,这个问题留到后面再说。
最后,C语言没有说用来表示符号的位是哪一位,这本来也不归C语言操心。甚至在计算机内部,二进制数是从高位写到低位、还是从低位写到高位异或是跳跃着写,各种计算机之间可能也不一致。
所以前面所描述的,只是一种原理上的可能性也是大多数情况下的现实性。就是说,多数情况就是这样,但具体到不同的计算机上,很可能是另外一种样子。当然后面还会补充更多的细节。
2.3.4 int类型值的范围
毫无疑问,在代码世界中,特别需要关注的是各个数据类型的值的范围。同时,这也是把问题世界中抽象归纳出的数据在代码中表示为何种数据类型的依据之一。
C语言没有具体规定与int类型对应的机器数具体应该是多少bits,但规定了至少应该是16bits。在不违背这条原则的条件下,编译器可以自行决定int类型占多少bits。
C语言同时要求编译器把自行确定的int类型的值的范围,并把本编译器int类型的最大值(INT_MAX)和最小值(INT_MIN),写在limits.h文件中。在TC 2.0中,INT_MAX和INT_MIN分别为32767和-32767-1。
而在Dev C++中这两个值分别为2147483647和-2147483647-1,这两个数对应着机器内部32bits二进制数(补码)的最大、最小值。如无特殊说明,本书后面的讲解均假设int类型在机器内部均为32bits二进制整数(补码)。
int类型值的范围表明的意义是,只有确信数据是在INT_MAX和INT_MIN之间的整数时,我们才可以要求计算机把这个数据按照int类型来处理。
比如说,在下面语句中:
%d要求后面的123在机器中是一个32bits的二进制数,123也的确是32bits,所以输出结果为:
而在下面语句中:
1234567890123显然不是[-2147483647-1,2147483647]这个区间内的整数,所以不可能被作为一个int类型的值以“%d”这种格式输出,最后的输出结果为:
显而易见,这是一个不知所云的结果。
然而在C语言中:
却并不是一种语法错误,这是非常值得注意的,编译器对此最多给出一个警告。编译通过和代码表达正确是两个完全不同的概念。
2.3.5 int类型常量在代码中的其他写法
前面提到过,在代码中直接写出的、正的十进制整数,如果值在1到编译器所标定的INT_MAX范围之内,那么就是int类型的常量。
int类型常量还有其他一些写法,这些写法也必须满足写出的值不大于INT_MAX。
1.八进制写法
int类型的常量也可以用八进制来写,要点如下。
八进制:规则是以0开头,只由0~7之间的数字组成。例如:
077
2.十六进制写法
用十六进制来写int类型常量必须以0X或0x开头,其后只由0~9之间的数字以及A~F(或a~f)之间的字母组成。其中A~F(或a~f)分别表示10到15。例如:
0X7FFF
3.常见误区
初学者经常会有以下几点误区。
(1)把0作为十进制int类型常量。
(2)把负数作为常量。
实际上十进制写int类型常量是不可以以0开头的,而且用十进制写int类型常量不容许0~9之外的任何字符,“-”当然也不例外。
无论是十进制、八进制还是十六进制的int类型的常量,值都必须是非负的,且在编译器所规定的int类型的值的范围之内。int类型的值的范围之外的情况后面会谈到,但无论如何在代码中是不可能写出负的int类型常量的。
练习
1.123、-456、0、-56是十进制int类型常量吗?
2.0123、-011、0182、01111111是八进制int类型常量吗?
3.0x123、-0x12、0XABCDEF123是十六进制int类型常量吗?
4.写出常量12345在代码中的八进制、十六进制写法。
2.3.6 Dev C++中int类型的机器数
在C语言中,各种数据类型所占的内存空间的多少以char类型占据的内存空间为单位度量。char类型占据的若干连续的二进制位被当做1个byte。
C语言用sizeof运算可以求出某种数据类型对应机器数占据多少byte。用法如下:
由于char类型占1个byte,所以sizeof(char)的值恒为1。
sizeof是个关键字,是唯一一个作为运算符的关键字。当sizeof求某种数据类型的长度时必须把数据类型名称用“()”括起来,如“sizeof(int)”。
sizeof也可以求某个数据所对应的数据类型占据的空间,这时可以用“()”把数据括起来也可以不用。如“sizeof 123”和“sizeof(123)”是完全等效的写法,表达的都是“sizeof(int)”的含义。
char类型占的位数对于不同的计算机也可能是不一样的,C语言要求编译器把这种类型所占的位数写在limits.h这个文件中。在Dev C++中,这个值(CHAR_BIT)是8。
由于在Dev C++中int类型的值可以是[-2147483647-1,2147483647]区间内的任何整数,所以可以推测出在Dev C++中int类型至少占32bits。通过代码可以确认这一点。
程序代码2-6
输出结果如下:
其中sizeof 2147483647表示求2147483647占几个char类型的长度;sizeof(-2147483647-1)表示求-2147483647-1占几个char类型的长度(byte)。代码中的“#include <limits.h>”是因为代码中用到的CHAR_BIT是在limits.h文件中定义的一个符号常量。
程序的运行结果表明,在Dev C++中int类型按照补码格式存储(32bits的反码或原码只能表示到-2147483647)。如无特殊说明,后面的讨论将以此作为前提假设。
虽然C语言并不要求计算机一定以补码的格式来表示整数。C语言也适用于釆用反码和原码的计算机。釆用哪种码制来表示整数是计算机的权利,而不可能出于C语言的要求。但编程者应该了解编程时所处的环境中int类型所对应的机器数需要的字节数和表示整数釆用的码制。
练习
将程序代码2-6中的sizeof(-2147483647-1)改为sizeof(-2147483648),上机运行程序,并记录结果。你能解释为什么sizeof(-2147483647-1)和sizeof(-2147483648)不同吗?