# 补充:原码反码补码和进制 ## 进制 进制也就是进位制,是人们规定的一种进位方法。 对于任何一种进制—X进制,就表示某一位置上的数运算时是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位。 | **十进制** | **二进制** | **八进制** | **十六进制** | | ---------- | ---------- | ---------- | ------------ | | 0 | 0 | 0 | 0 | | 1 | 1 | 1 | 1 | | 2 | 10 | 2 | 2 | | 3 | 11 | 3 | 3 | | 4 | 100 | 4 | 4 | | 5 | 101 | 5 | 5 | | 6 | 110 | 6 | 6 | | 7 | 111 | 7 | 7 | | 8 | 1000 | 10 | 8 | | 9 | 1001 | 11 | 9 | | 10 | 1010 | 12 | A | | 11 | 1011 | 13 | B | | 12 | 1100 | 14 | C | | 13 | 1101 | 15 | D | | 14 | 1110 | 16 | E | | 15 | 1111 | 17 | F | | 16 | 10000 | 20 | 10 | ### 二进制 二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”。 ```{note} 当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的。 ``` | **术语** | **含义** | | ------------ | ------------------------------------------------------------ | | bit(比特) | 一个二进制代表一位,一个位只能表示0或1两种状态。数据传输是习惯以“位”(bit)为单位。 | | Byte(字节) | 一个字节为8个二进制,称为8位,计算机中存储的最小单位是字节。数据存储是习惯以“字节”(Byte)为单位。 | | WORD(双字节) | 2个字节,16位 | | DWORD | 两个WORD,4个字节,32位 | | 1b | 1bit,1位 | | 1B | 1Byte,1字节,8位 | | 1k,1K | 1024 | | 1M(1兆) | 1024k, 1024*1024 | | 1G | 1024M | | 1T | 1024G | | 1Kb(千位) | 1024bit,1024位 | | 1KB(千字节) | 1024Byte,1024字节 | | 1Mb(兆位) | 1024Kb = 1024 * 1024bit | | 1MB(兆字节) | 1024KB = 1024 * 1024Byte | 十进制转化二进制的方法:用十进制数除以2,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果。 ![img](assets/clip_image002.png) 十进制的小数转换成二进制:小数部分和2相乘,取整数,不足1取0,每次相乘都是小数部分,顺序看取整后的数就是转化后的结果。 ![无标题](assets/clip_image002-1626218130024.jpg) ```{warning} 虽然计算机中所有数据都是以2进制方式存储的,但是在C语言中,是不能直接书写二进制的! ``` ### 八进制 八进制,Octal,缩写OCT或O,一种以8为基数的计数法,采用0,1,2,3,4,5,6,7八个数字,逢八进1。一些编程语言中常常以数字0开始表明该数字是八进制。 八进制的数和二进制数可以按位对应(八进制一位对应二进制三位),因此常应用在计算机语言中。 ![2016-06-01_172043](assets/clip_image002-1626218151369.jpg) 十进制转化八进制的方法: 用十进制数除以8,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果。 ![10转_副本1](assets/clip_image004.jpg) ### 十六进制 十六进制(英文名称:Hexadecimal),同我们日常生活中的表示法不一样,它由0-9,A-F组成,字母不区分大小写。与10进制的对应关系是:0-9对应0-9,A-F对应10-15。 十六进制的数和二进制数可以按位对应(十六进制一位对应二进制四位),因此常应用在计算机语言中。 ![2016-06-01_180102](assets/clip_image002-1626218169100.jpg) 十进制转化十六进制的方法: 用十进制数除以16,分别取余数和商数,商数为0的时候,将余数倒着数就是转化后的结果。 ![10转](assets/clip_image004-1626218169100.jpg) ### C语言如何表示相应进制数 | 十进制 | 以正常数字1-9开头,如123 | | -------- | ------------------------- | | 八进制 | 以数字0开头,如0123 | | 十六进制 | 以0x开头,如0x123 | | 二进制 | C语言不能直接书写二进制数 | ``` #include int main() { int a = 123; //十进制方式赋值 int b = 0123; //八进制方式赋值, 以数字0开头 int c = 0xABC; //十六进制方式赋值 //如果在printf中输出一个十进制数那么用%d,八进制用%o,十六进制是%x printf("十进制:%d\n",a ); printf("八进制:%o\n", b); //%o,为字母o,不是数字 printf("十六进制:%x\n", c); return 0; } ``` ## 计算机内存数值存储方式 ### 原码 一个数的原码(原始的二进制码)有如下特点: - 最高位做为符号位,0表示正,为1表示负 - 其它数值部分就是数值本身绝对值的二进制数 - 负数的原码是在其绝对值的基础上,最高位变为1 下面数值以1字节的大小描述: | **十进制数** | **原码** | | ------------ | --------- | | +15 | 0000 1111 | | -15 | 1000 1111 | | +0 | 0000 0000 | | -0 | 1000 0000 | 原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。 ### 反码 - 对于正数,反码与原码相同 - 对于负数,符号位不变,其它部分取反(1变0,0变1) | **十进制数** | **反码** | | ------------ | --------- | | +15 | 0000 1111 | | -15 | 1111 0000 | | +0 | 0000 0000 | | -0 | 1111 1111 | 反码运算也不方便,通常用来作为求补码的中间过渡。 ### 补码 **在计算机系统中,数值一律用补码来存储。** 补码特点: - 对于正数,原码、反码、补码相同 - 对于负数,其补码为它的反码加1 - 补码符号位不动,其他位求反,最后整个数加1,得到原码 | **十进制数** | **补码** | | ------------ | --------- | | +15 | 0000 1111 | | -15 | 1111 0001 | | +0 | 0000 0000 | | -0 | 0000 0000 | ``` #include int main() { int a = -15; printf("%x\n", a); //结果为 fffffff1 //fffffff1对应的二进制:1111 1111 1111 1111 1111 1111 1111 0001 //符号位不变,其它取反:1000 0000 0000 0000 0000 0000 0000 1110 //上面加1:1000 0000 0000 0000 0000 0000 0000 1111 最高位1代表负数,就是-15 return 0; } ``` #### 补码的意义 示例1:用8位二进制数分别表示+0和-0 | **十进制数** | **原码** | | ------------ | --------- | | +0 | 0000 0000 | | -0 | 1000 0000 | | **十进制数** | **反码** | | ------------ | --------- | | +0 | 0000 0000 | | -0 | 1111 1111 | 不管以原码方式存储,还是以反码方式存储,0也有两种表示形式。为什么同样一个0有两种不同的表示方法呢? 但是如果以补码方式存储,补码统一了零的编码: | **十进制数** | **补码** | | ------------ | ----------------------------------------------------- | | +0 | 0000 0000 | | -0 | 10000 0000由于只用8位描述,最高位1丢弃,变为0000 0000 | 示例2:计算9-6的结果 以原码方式相加: | **十进制数** | **原码** | | ------------ | --------- | | 9 | 0000 1001 | | -6 | 1000 0110 | ![2016-06-01_225835](assets/clip_image002-1626222358427.jpg) 结果为-15,不正确。 以补码方式相加: | **十进制数** | **补码** | | ------------ | --------- | | 9 | 0000 1001 | | -6 | 1111 1010 | ![2016-06-01_230938](assets/clip_image002-1626222366470.jpg) 最高位的1溢出,剩余8位二进制表示的是3,正确。 **在计算机系统中,数值一律用补码来存储**,主要原因是: - 统一了零的编码 - 将符号位和其它位统一处理 - 将减法运算转变为加法运算 - 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃 ### 数值溢出 当超过一个数据类型能够存放最大的范围时,数值会溢出。 有符号位最高位溢出的区别:符号位溢出会导致数的正负发生改变,但最高位的溢出会导致最高位丢失。 | **数据类型** | **占用空间** | **取值范围** | | ------------- | ------------ | ----------------------- | | char | 1字节 | -128到 127(-27 ~ 27-1) | | unsigned char | 1字节 | 0 到 255(0 ~ 28-1) | ``` #include int main() { char ch; //符号位溢出会导致数的正负发生改变 ch = 0x7f + 2; //127+2 printf("%d\n", ch); // 0111 1111 //+2后 1000 0001,这是负数补码,其原码为 1111 1111,结果为-127 //最高位的溢出会导致最高位丢失 unsigned char ch2; ch2 = 0xff+1; //255+1 printf("%u\n", ch2); // 1111 1111 //+1后 10000 0000, char只有8位最高位的溢出,结果为0000 0000,十进制为0 ch2 = 0xff + 2; //255+1 printf("%u\n", ch2); // 1111 1111 //+1后 10000 0001, char只有8位最高位的溢出,结果为0000 0001,十进制为1 return 0; } ``` ## 类型限定符 | **限定符** | **含义** | | ---------- | ------------------------------------------------------------ | | extern | 声明一个变量,extern声明的变量没有建立存储空间。 extern int a;//变量在定义的时候创建存储空间 | | const | 定义一个常量,常量的值不能修改。 const int a = 10; | | Volatile | 防止编译器优化代码 | | register | 定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令,如果CPU有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。 |