常量是以什么样的二进制代码存储在计算机中的
在计算机中不管什么数据都是以二进制的形式存储的,因为计算机只认识“0”和“1”。只不过不同类型的数据存储在计算机中时转化为二进制的规则不一样,这个问题实际上就是编码的问题。比如说:
int i = 86;
该语句的意思是直接将十进制数86放到变量i中吗?不是,而是将86的二进制代码放进去。那么它到底是以什么形式的二进制代码放进去的呢?整数是以补码的形式转化为二进制代码存储在计算机中的,而实数是以IEEE 754标准转化为二进制代码存储在计算机中的。字符的存储方式本质上与整数的存储方式相同。如字符’A’,它是先通过ASCII码转化为一个十进制整数,然后就同十进制整数的存储一样了。
原码和反码
讲述补码之前首先要了解两个预备知识:原码和反码。原码也叫“符号绝对值”码,最高位0表示正,1表示负,其余二进制数是该数字绝对值的二进制数。正整数的原码就是它本身的二进制数,比如5的原码就是0101。那么-5的原码呢?负号用1表示,-5的绝对值是5,5的二进制是0101,所以-5的原码就是10101。但是在计算机中-5不是这么存储的,这也是我们为什么要讲补码的原因。计算机中所有的整数都是以补码的形式存储的。
原码虽然简单、易懂,但因为存在加、减、乘、除四种运算,且加减运算复杂,使用原码增加了CPU的复杂度,使CPU设计起来比较困难。而且如果用原码的话,0的表示不唯一,可以是正0,也可以是负0,你可以写成100000,也可以写成000000。
而补码不一样,使用补码则减法、乘法都可以用加法进行处理,所以用补码比用原码好。原码在计算机里面从来没有被使用过。而反码就是所有位都取反,1变成0,0变成1。反码的运算也很不方便,也没有在计算机中用过。
十进制数转换为二进制补码
正整数的补码与原码相同。
负整数补码求法:先求该负数绝对值的二进制数,然后将所有位取反,末位加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
零的补码是唯一的,全是0。
二进制补码转换为十进制数
已知一个数的补码,求原码的操作其实就是对该补码再求补码:
- 如果最高位是0,则表明是正整数,原码和补码相同。比如补码为:
00000000000000000000000000001111
最高位是0,表明这个数是正整数。则原码和补码相同,所以直接转换成十进制数为15。 - 如果最高位是1,则表明是负整数,则原码为:将所有位取反(不管是符号位还是有效数字位),然后末位加1。此时得到的是无符号型的二进制数,即所有位数都是数字位,没有符号位。所以算出来的肯定是一个正整数,这个正整数的相反数就是该补码对应的十进制数。比如补码:11111111111111111111111111111000最高位是1,表明这个数是负整数。则将包括最高符号位在内的所有位全部取反得:00000000000000000000000000000111然后末位加1得:00000000000000000000000000001000此时就得到了一个正整数,直接转换成十进制数为8,所以补码所对应的十进制数为-8。
int型变量所能存储的范围
int型变量占4字节,就是32位,所以它所能存储的最大的正整数是01111111111111111111111111111111,即2147483647。注意,最高位是符号位,正数用0表示。那么为什么最大整数是01111111111111111111111111111111?再加1会怎样?我们来试一下,二进制逢二进一,再加1就变成10000000000000000000000000000000。最左边是1,就变成负数了,所以不能再大了。这个负数的值是-2147483648,它是int型变量所能存储的最小的负整数。
short类型变量占两字节,就是16位,它所能存储的最大的正整数是0111111111111111,把它转换成十进制数就是32767。如果再加1就会变成1000000000000000,最左边是1,就变成负数了。这个负数的值是-32768,它是short类型变量所能存储的最小的负数。
#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;
}
如果是unsigned short,那么就没有符号位,所有位都是数字位,它所能存储的最大的数是1111111111111111,即65535,所能存储的最小的数就是全零,即0。也可以写一个程序看一下:
# include<stdio.h>
int main(void)
{
unsigned short a, b;
a = 65535;
b = a + 1;
printf("b = %d\n", b);
return 0;
}
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位,它最大所能存储的正整数是01111111,即127。现在将int型的整数128赋予它,它放不下,这个叫“溢出”。那么溢出的话输出会是多少呢?128是int型,占4字节,它的二进制代码为00000000000000000000000010000000。但是现在要将它赋给一个只有8位的char型变量,所以只能将低8位的10000000放进去,其他的都会被截掉。整数在计算机中都是以补码的形式存储的,此时10000000在“计算机的眼里”是一个补码。最左边是1表示是负数。补码10000000所对应的十进制数是-128,所以最后输出的就是-128。因此溢出会使最大正整数变成最小负整数。
在C语言中,字符型、整型、浮点型都是可以相互赋值的,因为它们在计算机中虽然二进制编码方式不同,但归根到底都是二进制。但是除非是特殊需要,不同数据类型之间最好不要相互赋值转换。需要存储什么类型的数据就定义什么类型的变量。