购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.8 补码

补码很简单,但是很重要。通过对补码的学习一定要弄明白以下几个问题:

1)int型变量所能存储的数字的范围是多少?

2)最小负整数的二进制代码是多少?

3)最大正整数的二进制代码是多少?

4)数字超过最大正整数会怎样?

5)int型变量和char型变量是如何相互赋值的?

5.8.1 原码和反码

讲述补码之前首先要了解两个预备知识:原码和反码。

原码也叫“符号绝对值”码,最高位0表示正,1表示负,其余二进制数是该数字绝对值的二进制数。正整数的原码就是它本身的二进制数,比如5的原码就是0101。那么–5的原码呢?负号用1表示,–5的绝对值是5,5的二进制是0101,所以–5的原码就是10101。

但是在计算机中–5不是这么存储的,这也是我们为什么要讲补码的原因。计算机中所有的整数都是以补码的形式存储的。原码虽然简单、易懂,但因为存在加、减、乘、除四种运算,且加减运算复杂,使用原码增加了CPU的复杂度,使CPU设计起来比较困难。而且如果用原码的话,0的表示不唯一,可以是正0,也可以是负0,你可以写成100000,也可以写成000000。

而补码不一样,使用补码则减法、乘法都可以用加法进行处理,所以用补码比用原码好。原码在计算机里面从来没有被使用过。

而反码就是所有位都取反,1变成0,0变成1。反码的运算也很不方便,也没有在计算机中用过。

所以对于原码和反码大家不用深究,只需要知道什么是原码,什么是反码就行了,因为学习补码的时候要用到这两个知识点。但是不管是原码还是补码,都是最高位如果是0则表示正数,是1则表示负数,这一点很重要,一定要记住。

5.8.2 补码的两个核心问题

补码主要是用来解决整数的存储,包括0、正整数和负整数。在计算机中,整数都是以补码的形式存储的。不要以为补码很复杂,关于补码主要有两个问题,将这两个问题弄清楚了,补码自然而然就掌握了:一是如何将一个十进制数转换成它的二进制补码;二是如何将一个二进制补码转换成对应的十进制数。

(1)十进制数转换为二进制补码

1)正整数的补码与原码相同。

2)负整数补码求法:先求该负数绝对值的二进制数,然后将所有位取反,末位加1,不够位数时左边补1。比如int型的–3,因为–3的绝对值是3,int型占32位,所以int型3的二进制代码为:

0000 0000 0000 0000 0000 0000 0000 0011

然后所有位取反,就是:

1111 1111 1111 1111 1111 1111 1111 1100

最后末位加1就是:

1111 1111 1111 1111 1111 1111 1111 1101

这就是int型–3在计算机中存储的二进制形式。计算机的“附件”中都有“计算器”,大家可以算一下。只不过int型是4字节,所以在计算时计算机类型要选“双字”。如图5-2所示。

图5-2 使用“计算器”进行计算

图5-2中左图到右图只需在二进制下输完之后直接选择“十进制”即可显示出其对应的十进制数。从显示的结果也可以进一步论证“计算机中整数是以补码的形式存储的”。你也可以反过来输入,先输入十进制数–3,然后查看二进制结果。还是注意要选择“双字”,因为int是32位的。

3)零的补码是唯一的,全是0。

(2)二进制补码转换为十进制数

已知一个数的补码,求原码的操作其实就是对该补码再求补码:

1)如果最高位是0,则表明是正整数,原码和补码相同。比如补码为:

0000 0000 0000 0000 0000 0000 0000 1111

最高位是0,表明这个数是正整数。则原码和补码相同,所以直接转换成十进制数为15。

2)如果最高位是1,则表明是负整数,则原码为:将所有位取反(不管是符号位还是有效数字位),然后末位加1。此时得到的是无符号型的二进制数,即所有位数都是数字位,没有符号位。所以算出来的肯定是一个正整数,这个正整数的相反数就是该补码对应的十进制数。

比如补码:

1111 1111 1111 1111 1111 1111 1111 1000

最高位是1,表明这个数是负整数。则将包括最高符号位在内的所有位全部取反得:

0000 0000 0000 0000 0000 0000 0000 0111

然后末位加1得:

0000 0000 0000 0000 0000 0000 0000 1000

此时就得到了一个正整数,直接转换成十进制数为8,所以补码所对应的十进制数为–8。

又比如补码:

1000 0000 0000 0000 0000 0000 0000 0000

最高位是1,表明这个数是负整数。则将包括最高位符号位在内的所有位全部取反得:

0111 1111 1111 1111 1111 1111 1111 1111

然后末位加1得:

1000 0000 0000 0000 0000 0000 0000 0000

此时虽然最高位是1但它表示的不是符号位,而是数字位。该二进制代码所表示的十进制数为2 31 ,即2147483648。所以补码所对应的十进制数就是该数的相反数,即–2147483648。

同样大家可以用计算器算一下。

3)如果全是0,则对应的十进制数就是0。

5.8.3 int型变量所能存储的范围

下面来看一个int型变量所能存储的数字的范围是多少。int型变量占4字节,就是32位,所以它所能存储的最大的正整数是0111 1111 1111 1111 1111 1111 1111 1111,即2147483647。注意,最高位是符号位,正数用0表示。那么为什么最大整数是0111 1111 1111 1111 1111 1111 1111 1111?再加1会怎样?我们来试一下,二进制逢二进一,再加1就变成1000 0000 0000 0000 0000 0000 0000 0000。最左边是1,就变成负数了,所以不能再大了。这个负数的值是–2147483648,我们在上一节算过。它是int型变量所能存储的最小的负整数。

我们前面讲过short类型变量占两字节,就是16位,它所能存储的最大的正整数是0111 1111 1111 1111,把它转换成十进制数就是32767。如果再加1就会变成1000 0000 0000 0000,最左边是1,就变成负数了。这个负数的值是–32768,大家可以自己算一下,它是short类型变量所能存储的最小的负数。

写一个程序看一下:


/*
    时间:2015101516:11:31
*/
# include <stdio.h>
int main(void)
{
    short a, b;
    int c, d;
    a = 32767;
    c = 2147483647;
    b = a + 1;
    d = c + 1;
    printf("b = %d\n", b);
    printf("d = %d\n", d);
    return 0;
}
/*VC++ 6.0中的输出结果是:
--------------------------------------
b = -32768
d = -2147483648
--------------------------------------
*/

如果是unsigned short,那么就没有符号位,所有位都是数字位,它所能存储的最大的数是1111 1111 1111 1111,即65535,所能存储的最小的数就是全零,即0。也可以写一个程序看一下:


/*
    时间:2015101516:29:57
*/
# include <stdio.h>
int main(void)
{
    unsigned short a, b;
    a = 65535;
    b = a + 1;
    printf("b = %d\n", b);
    return 0;
}
/*VC++ 6.0中的输出结果是:
--------------------------------------
b = 0
--------------------------------------
*/

5.8.4 int型和char型变量是如何相互赋值的

整型和字符型是互通的,它们在内存中存储的本质是相同的,只是存储的范围不同而已。整型可以是2字节、4字节、8字节,而字符型只占1字节。下面直接写一个程序:


# include <stdio.h>
int main(void)
{
    int i = 128;
    char ch = i;
    printf("%d\n", ch);
    return 0;
}

大家想想输出的结果会是几?char类型占1字节,就是8位,它最大所能存储的正整数是0111 1111,即127。现在将int型的整数128赋予它,它放不下,这个叫“溢出”。那么溢出的话输出会是多少呢?128是int型,占4字节,它的二进制代码为0000 0000 0000 0000 0000 0000 1000 0000。但是现在要将它赋给一个只有8位的char型变量,所以只能将低8位的1000 0000放进去,其他的都会被截掉。整数在计算机中都是以补码的形式存储的,此时1000 0000在“计算机的眼里”是一个补码。最左边是1表示是负数。补码1000 0000所对应的十进制数是–128,所以最后输出的就是–128。因此溢出会使最大正整数变成最小负整数。

在C语言中,字符型、整型、浮点型都是可以相互赋值的,因为它们在计算机中虽然二进制编码方式不同,但归根到底都是二进制。但是除非是特殊需要,不同数据类型之间最好不要相互赋值转换。需要存储什么类型的数据就定义什么类型的变量。 ZVNguGdjM0qx8wggpV6KD0BX5ybzF4+ZyqiLCxrN2M1Hei2kiygPK+girz3+wAKA

点击中间区域
呼出菜单
上一章
目录
下一章
×