Loading... | | 整数 | | | - | - | - | | | 概念 | 10 | | 原码 | 数据的二进制形式 | 0000 1010 | | 反码 | 就是原码 | 0000 1010 | | 补码 | 就是原码 | 0000 1010 | --- | | 整数 | | | - | - | - | | | 概念 | -10 | | 原码 | 数据的二进制形式 | 1000 1010 | | 反码 | 原码的<span style="color:#A52A2A">符号位</span>不变,其他取反 | 1111 0101 | | 补码 | 反码+1 | 1111 0110 | 注意:负数原码第一位是1,第一位就是符号位。 --- 注:图标以一个字节为例。 * 注意: > 无符号数、正数: > > 原码==反码==补码 > > --- > > 负数: > > 反码==原码的<span style="color:#DC143C">符号位不变</span> 其他取反 > > 补码==反码<span style="color:#DC143C">+1</span> * 重要:任何数据在计算机中都是以补码方式存储。(负数在计算机中以补码方式存储,整数的补码==原码) ## 计算机 为什么要 补码? > 6 - 10== -4 > > 大家都同意吧? > > --- > > 那可不可以看成这样? > > 6 + (-10)== -4 > > --- > > 还可以二进制(其中-10的二进制是 1000 1010,前面说了的)。 > > --- > > 如果没有补码的话我们看: > > 0000 0110 > > 1000 1010(假设没有补码) > > —————— > > 1001 0000 == -16(错误) > > --- > > 如果有补码: > > 0000 0110 > > 1111 0110(负数写补码) > > —————— > > 1111 1100 这是负数,我们取反加1。 > > 所以: 原码==1000 0011 +1==1000 0100 = -4 注:都是以一字节分析。 ## 补码的分析: 补码的意义:将减法的运算 变加法运算。 负数:二进制第一位是符号位,为1。 --- 有符号数:<span style="color:#DC143C">1</span>111 1111 ~<span style="color:#DC143C"> 1</span>000 0000 ~ <span style="color:#DC143C">0</span>000 0000 ~ <span style="color:#DC143C">0</span>111 1111 -> (-127 ~ -0 ~ +0 ~ +127)共256个数 计算机为了扩展数据的表示范围,故意将-0看成-128。 所以一个符号位字节数有效范围是 -128 ~ 127 --- 无符号数: 0000 0000 ~ 1111 1111 ->(0 ~ 255) 共256个数 --- 我们得到的范围长度都是256,是应为一个字节的表示范围大小。 --- 总结:补码<span style="color:#DC143C">统一</span>了 0 的编码。 +0 == <span style="color:#DC143C">0</span>000 0000 ==0000 0000 (反码) == 0000 0000(补码) -0 == 1000 0000 == 1111 1111 (反码) == 0000 0000 (补码)!=10000 0000(错误) 最前面的 1 溢出了,只有8位。所以最后结果为 0000 0000 --- <div class="tip inlineBlock success"> 总结:补码将<span style="color:#DC143C">减法</span>运算 变 <span style="color:#DC143C">加法</span>运算 同时统一了<span style="color:#DC143C">0的编码</span>。 </div> ## 计算机对数据的存储 负数存储: ```C #include<stdio.h> void fivk() { //负数 是以补码 存储 char data = -10; //取 %x(16进制) %u(无符号) %o(8进制) 都是输出内存的原样数据 //每4位二进制 代表 一位16进制 //应为 4位二进制范围是 0000 ~ 1111 == 0 ~ 15 == 1位16进制的范围 printf("%x", data);// 0xf6 == 1111 0110 } int main(int argc, char argv[]) { fivk(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/505484529.png) ![](https://blog.fivk.cn/usr/uploads/2021/03/3686154868.png) * 应为 我们只分析1个字节,char占4个字节,%x输出整型,只看最后的f6 * 说明了负数存储的是我们的补码。 --- 正数存储: ```C #include<stdio.h> void fivk() { //负数 是以补码 存储 char data = 10; //取 %x(16进制) %u(无符号) %o(8进制) 都是输出内存的原样数据 //每4位二进制 代表 一位16进制 //应为 4位二进制范围是 0000 ~ 1111 == 0 ~ 15 == 1位16进制的范围 printf("%x", data);// 0x0a == 0000 1010A } int main(int argc, char argv[]) { fivk(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/791287563.png) ![](https://blog.fivk.cn/usr/uploads/2021/03/507044694.png) * 为什么这里a前面不补位呢??? 为什么不是ffff ff0a呢?这是应为%x补位讲究的是根据内存中的实际数据,内存为1,我们补1,内存为0,就补0。所以这补的是 0000 000a 前面0不显示。 * 十六进制存储: ```C #include<stdio.h> void fivk() { char data = 0xae;//0xae == 1010 1110 printf("%x", data); } int main(int argc, char argv[]) { fivk(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/3288584223.png) 我们本想打印ae但是他会自动补位 > 如果以16进制赋值,则是以原码存储 * 八进制存储: ```C #include<stdio.h> void fivk() { char data = 0256;//0256 == 1010 1110 // 0 ~ 7 有8位 // 000 ~ 111 有8位 //所以 每3位二进制代表1位八进制 printf("%x", data); } int main(int argc, char argv[]) { fivk(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/2224041634.png) 同样的,自动补位了f > 这里发现8进制赋值,也是以原码存储 * 数据越界存储: ```C #include<stdio.h> void fivk() { char data = 129;//129超过一个字节范围 -128 ~ 127 //129 == 1000 0001 printf("%x", data); } int main(int argc, char argv[]) { fivk(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/893873586.png) 前面的f是补位,不说了。 > 如果数据越界,以原码存储、 * 无符号以负数存储: ```C #include<stdio.h> void fivk() { unsigned char data = -10; printf("%x", data); } int main(int argc, char argv[]) { fivk(); return 0; } ``` <div class='album_block'> [album type="photos"] ![正数](https://blog.fivk.cn/usr/uploads/2021/03/1438782343.png) ![负数](https://blog.fivk.cn/usr/uploads/2021/03/1465618587.png) [/album] </div> <div class="tip inlineBlock warning"> 无符号数存正数是一样的,关键我们看看存负数。运行结果是f6,和有符号的是一样的。所以说,计算机存储数据的时候它只在乎右边这个数据是什么。如果是负数就转换成补码,和前面的符号无关。以为,在对data赋值之前,-10先转换为补码再赋值的。我们可以看成等号赋值之前已经完成了补码的转换。 </div> <div class="tip inlineBlock success"> 综上:正数、16进制、8进制、越界、无符号负数 都是以原码存储,只有负数以补码存储。 </div> ## 计算机对数据的读取 * %d %hd %ld(带有d的)是有符号取 * %u %x %o %lu 是无符号取 > 如果是有符号取:首先看内存的最高位(符号位)。如果为1,它认为取到的是某个数的补码,需要转换成原码(先求反码->得到补码->求出原码)。如果为0,直接将数据原样输出。 ```C #include<stdio.h> void fivk02() { char data1 = -10; //取 //%d %hd %ld(带有d的)是有符号取 //%u %x %o %lu都是无符号取 printf("%d\n", data1); //有符号输出 先判断是否是补码 printf("%u\n", data1 & 0xff); //无符号输出 直接原样输出 //后面加 &0xff 表示只取低 8 位 } int main(int argc, char argv[]) { fivk02(); return 0; } ``` ![](https://blog.fivk.cn/usr/uploads/2021/03/3494820843.png) ![](https://blog.fivk.cn/usr/uploads/2021/03/2105577845.png) 最后修改:2021 年 03 月 20 日 © 禁止转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏