1.计算机预备知识

计算机体系结构简介

参考资料:
C语言中文网:http://c.biancheng.net/
《C语言编程魔法书基于C11标准》
视频教程:C语音深入剖析班(国嵌 唐老师主讲)

中央处理器

中央处理器也就是我们常说的CPU,买电脑时,电脑贵不贵,除去独立显卡,最贵就是它了,它也决定了我们电脑的处理能力。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibkYKh3A-1580913591311)(img\CPU.jpg)]

寄存器

寄存器是在CPU核心中的,用于暂存内存交过来的数据(当然还有CPU的高速缓存区,这里先不做介绍),寄存器的处理速度非常的快,比内存快了很多很多,CPU是不能直接操作内存的,是要通过寄存器提交出去的,这个寄存器就相当于皇上身边的小太监,大臣上奏的东西要通过太监上传给皇上,皇上批阅完后又通过太监传回给大臣。

贮存器

贮存器也就是我们电脑中的硬盘,这种贮存器可以把数据持久保存,不至于在断电的时候就丢失数据,我们电脑上的所有数据都存放在这里,贮存器也有机械和固态之分,也就是我们常说的机械硬盘和固态硬盘了,但该器件在电脑中的运行速度实在是太慢了,根本跟不上CPU的处理速度,所以就有了内存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yP84Q5oZ-1580913591312)(img\硬盘.jpg)]

存储器

储存器一般是指我们常说的内存,这个内存也就是RAM,也就是我们常说的运行内存,这个一般有2G,4G,8G,16G等之分,内存越大能运行的东西就越多,因为内存距离CPU比硬盘更近一步,所以内存的速度是硬盘的好几倍或几十倍,但内存的数据是会关机后就会丢失的,所以为什么手机或电脑用久了卡重启后就不卡的原因,因为在运行的时候开启了太多没用的程序大量占用内存,而没有多余的内存去运行其它程序,所以造成了卡顿。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gxU5tZpl-1580913591313)(img\内存.jpg)]

进制转换

二进制、八进制和十六进制

二进制

在计算机内部,数据都是以二进制的形式存储的,二进制是学习编程必须掌握的基础。

二进制加减法和十进制加减法的思想是类似的:

  • 对于十进制,进行加法运算时逢十进一,进行减法运算时借一当十;
  • 对于二进制,进行加法运算时逢二进一,进行减法运算时借一当二。

下面两张示意图详细演示了二进制加减法的运算过程。

  1. 二进制加法:1+0=1、1+1=10、11+10=101、111+111=1110

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zAK6TcOK-1580913591314)(img/1.png)]

  1. 二进制减法:1-0=1、10-1=1、101-11=10、1100-111=101

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9gWWBHPw-1580913591315)(img/2.png)]

八进制

除了二进制,C语言还会使用到八进制。

八进制有 0~7 共8个数字,基数为8,加法运算时逢八进一,减法运算时借一当八。例如,数字 0、1、5、7、14、733、67001、25430 都是有效的八进制。

下面两张图详细演示了八进制加减法的运算过程。

  1. 八进制加法:3+4=7、5+6=13、75+42=137、2427+567=3216

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYGTCT99-1580913591316)(img/3.png)]

  1. 八进制减法:6-4=2、52-27=23、307-141=146、7430-1451=5757

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28m6cQ92-1580913591316)(img/4.png)]

十六进制

除了二进制和八进制,十六进制也经常使用,甚至比八进制还要频繁。

十六进制中,用A来表示10,B表示11,C表示12,D表示13,E表示14,F表示15,因此有 0~F 共16个数字,基数为16,加法运算时逢16进1,减法运算时借1当16。例如,数字 0、1、6、9、A、D、F、419、EA32、80A3、BC00 都是有效的十六进制。

注意:十六进制中的字母不区分大小写,ABCDEF 也可以写作 abcdef。

下面两张图详细演示了十六进制加减法的运算过程。

  1. 十六进制加法:6+7=D、18+BA=D2、595+792=D27、2F87+F8A=3F11

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JdFifEVO-1580913591317)(img/5.png)]

  1. 十六进制减法:D-3=A、52-2F=23、E07-141=CC6、7CA0-1CB1=5FEF

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TuRJzaTx-1580913591319)(img/6.png)]

进制转换

转为十进制

二进制、八进制和十六进制向十进制转换都非常容易,就是“按权相加”。

所谓“权”,也即“位权”。

假设当前数字是 N 进制,那么:

  • 对于整数部分,从右往左看,第 i 位的位权等于Ni-1
  • 对于小数部分,恰好相反,要从左往右看,第 j 位的位权为N-j。

更加通俗的理解是,假设一个多位数(由多个数字组成的数)某位上的数字是 1,那么它所表示的数值大小就是该位的位权。

整数部分

例子:将八进制数字53627转换成十进制

53627 = 5×8^4 + 3×8^3 + 6×8^2 + 2×8^1 + 7×8^0 = 22423(十进制)

从右往左看,第1位的位权为 8^0=1,第2位的位权为 8^1=8,第3位的位权为 8^2=64,第4位的位权为 8^3=512,第5位的位权为 8^4=4096 …… 第n位的位权就为 8^n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

例子,将十六进制数字9FA8C转换成十进制

9FA8C = 9×16^4 + 15×16^3 + 10×16^2 + 8×16^1 + 12×16^0 = 653964(十进制)

例子,将二进制数字转换成十进制

11010 = 1×2^4 + 1×2^3 + 0×2^2 + 1×2^1 + 0×2^0 = 26(十进制)

小数部分

例子,将八进制数字423.5176转换成十进制:

423.5176 = 4×82 + 2×81 + 3×80 + 5×8-1 + 1×8-2 + 7×8-3 + 6×8-4 = 275.65576171875(十进制)

小数部分和整数部分相反,要从左往右看,第1位的位权为 8^-1=1/8,第2位的位权为 8^-2=1/64,第3位的位权为 8^-3=1/512,第4位的位权为 8^-4=1/4096 …… 第m位的位权就为 8^-m。

例子,将二进制数字1010.1101转换成十进制:

1010.1101 = 1×2^3 + 0×2^2 + 1×2^1 + 0×2^0 + 1×2^-1 + 1×2^-2 + 0×2^-3 + 1×2^-4 = 10.8125(十进制)

将十进制转换为其它进制

整数部分

十进制整数转换为 N 进制整数采用“除 N 取余,逆序排列”法。具体做法是:

  • 将 N 作为除数,用十进制整数除以 N,可以得到一个商和余数;
  • 保留余数,用商继续除以 N,又得到一个新的商和余数;
  • 仍然保留余数,用商继续除以 N,还会得到一个新的商和余数;
  • ……
  • 如此反复进行,每次都保留余数,用商接着除以 N,直到商为 0 时为止。

把先得到的余数作为 N 进制数的低位数字,后得到的余数作为 N 进制数的高位数字,依次排列起来,就得到了 N 进制数字。

下图演示将十进制数字36926转换成八进制的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cGg9MaD-1580913591319)(img/7.png)]

从图中得知,十进制数字 36926 转换成八进制的结果为 110076。

将十进制数字 42 转换成二进制的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55Bp1xYa-1580913591320)(img/8.png)]

十进制数字 42 转换成二进制的结果为 101010。

小数部分

十进制小数转换成 N 进制小数采用“乘 N 取整,顺序排列”法。具体做法是:

  • 用 N 乘以十进制小数,可以得到一个积,这个积包含了整数部分和小数部分;
  • 将积的整数部分取出,再用 N 乘以余下的小数部分,又得到一个新的积;
  • 再将积的整数部分取出,继续用 N 乘以余下的小数部分;
  • ……
  • 如此反复进行,每次都取出整数部分,用 N 接着乘以小数部分,直到积中的小数部分为 0,或者达到所要求的精度为止。

把取出的整数部分按顺序排列起来,先取出的整数作为 N 进制小数的高位数字,后取出的整数作为低位数字,这样就得到了 N 进制小数。

将十进制小数 0.930908203125 转换成八进制小数的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xxQbVjX8-1580913591321)(img/10.png)]

十进制小数 0.930908203125 转换成八进制小数的结果为 0.7345。

将十进制小数 0.6875 转换成二进制小数的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LGCctHHi-1580913591322)(img/11.png)]

十进制小数 0.6875 转换成二进制小数的结果为 0.1011。

如果一个数字既包含了整数部分又包含了小数部分,那么将整数部分和小数部分开,分别按照上面的方法完成转换,然后再合并在一起即可。例如:

  • 十进制数字 36926.930908203125 转换成八进制的结果为 110076.7345;
  • 十进制数字 42.6875 转换成二进制的结果为 101010.1011。

注意

十进制小数转换成其他进制小数时,结果有可能是一个无限位的小数。请看下面的例子:

  • 十进制 0.51 对应的二进制为 0.100000101000111101011100001010001111010111…,是一个循环小数;
  • 十进制 0.72 对应的二进制为 0.1011100001010001111010111000010100011110…,是一个循环小数;
  • 十进制 0.625 对应的二进制为 0.101,是一个有限小数。

二进制转八进制、十六进制

1) 二进制整数和八进制整数之间的转换

二进制整数转换为八进制整数时,每三位二进制数字转换为一位八进制数字,运算的顺序是从低位向高位依次进行,高位不足三位用零补齐。下图演示了如何将二进制整数 1110111100 转换为八进制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxTnB2vO-1580913591323)(img/12.png)]

二进制整数 1110111100 转换为八进制的结果为 1674。

八进制整数转换为二进制整数时,思路是相反的,每一位八进制数字转换为三位二进制数字,运算的顺序也是从低位向高位依次进行。下图演示了如何将八进制整数 2743 转换为二进制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwSOuTOT-1580913591323)(img/13.png)]

八进制整数 2743 转换为二进制的结果为 10111100011。

2) 二进制整数和十六进制整数之间的转换

二进制整数转换为十六进制整数时,每四位二进制数字转换为一位十六进制数字,运算的顺序是从低位向高位依次进行,高位不足四位用零补齐。下图演示了如何将二进制整数 10 1101 0101 1100 转换为十六进制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P7oDa12S-1580913591324)(img/14.png)]

二进制整数 10 1101 0101 1100 转换为十六进制的结果为 2D5C。

十六进制整数转换为二进制整数时,思路是相反的,每一位十六进制数字转换为四位二进制数字,运算的顺序也是从低位向高位依次进行。下图演示了如何将十六进制整数 A5D6 转换为二进制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xX0ybEbY-1580913591326)(img/15.png)]

十六进制整数 A5D6 转换为二进制的结果为 1010 0101 1101 0110。

数据在内存中的存储

整数的存储方式

源码

将一个整数转换成二进制形式,就是其原码。例如66 的原码就是0000 0000 0000 0110-18原码就是1000 0000 0001 0010

通俗的理解,原码就是一个整数本来的二进制形式。

反码

谈到反码,正数和负数要区别对待,因为它们的反码不一样。对于正数,它的反码就是其原码(原码和反码相同);负数的反码是将原码中除符号位以外的所有位(数值位)取反,也就是 0 变成 1,1 变成 0。例如6 的原码和反码都是0000 0000 0000 0110-18,此时 -18 的反码是1111 1111 1110 1101

补码

正数和负数的补码也不一样,也要区别对待。对于正数,它的补码就是其原码(原码、反码、补码都相同);负数的补码是其反码加 1。例如66 的原码、反码、补码都是0000 0000 0000 0110-18,此时 -18 的补码是1111 1111 1110 1110。可以认为,补码是在反码的基础上打了一个补丁,进行了一下修正,所以叫“补码”。

原码、反码、补码的概念只对负数有实际意义,对于正数,它们都一样。

最后我们总结一下 6 和 -18 从原码到补码的转换过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeTLmEZp-1580913591326)(img/18.jpg)]

在计算机内存中,整数一律采用补码的形式来存储。这意味着,当读取整数时还要采用逆向的转换,也就是将补码转换为原码。将补码转换为原码也很简单:先减去 1,再将数值位取反即可。

小数的存储方式

小数在内存中以科学计数法的形式来存储,具体形式为:

flt = (-1)^sign × mantissa × base^exponent
要表示的小数 = (-1)^正负号(0为正,1为负) × 尾数(精度) × 基数(进制数)^指数

对各个部分的说明:

  • flt 是要表示的小数。
  • sign 用来表示 flt 的正负号,它的取值只能是 0 或 1:取值为 0 表示 flt 是正数,取值为 1 表示 flt 是负数。
  • mantissa 为尾数,或者说精度,是 base 进制的小数,并且 1 ≤ mantissa < base,这意味着,小数点前面只能有一位数字;
  • base 是基数,或者说进制,它的取值大于等于 2(例如,2 表示二进制、10 表示十进制、16 表示十六进制……)。数学中常见的科学计数法是基于十进制的,例如 6.93 × 10^13;计算机中的科学计数法可以基于其它进制,例如 1.001 × 2^7 就是基于二进制的,它等价于 1001 0000。
  • exponent 为指数,是一个整数,可正可负,并且为了直观一般采用十进制表示。

下面我们以 19.625 为例来演示如何将小数转换为浮点格式。

当 base 取值为 10 时,19.625 的浮点形式为:

19.625 = 1.9625 × 10^1

当 base 取值为 2 时,将 19.625 转换成二进制为 10011.101,用浮点形式来表示为:

19.625 = 10011.101 = 1.0011101×2^4
19.625 整数部分的二进制形式为:
19 = 1×2^4 + 0×2^3 + 0×2^2 + 1×2^1 + 1×2^0 = 10011
小数部分的二进制形式为:
0.625 = 1×2^-1 + 0×2^-2 + 1×2^-3 = 101
将整数部分和小数部分合并在一起:
19.625 = 10011.101

可以看出,当基数(进制)base 确定以后,指数 exponent 实际上就成了小数点的移动位数:

  • exponent 大于零,mantissa 中的小数点右移 exponent 位即可还原小数的值;
  • exponent 小于零,mantissa 中的小数点左移 exponent 位即可还原小数的值。

换句话说,将小数转换成浮点格式后,小数点的位置发生了浮动(移动),并且浮动的位数和方向由 exponent 决定,所以我们将这种表示小数的方式称为浮点数。

二进制形式的浮点数的存储

原则上讲,上面的科学计数法公式中,符号 sign、尾数 mantissa、基数 base 和指数 exponent 都是不确定因素,都需要在内存中体现出来。但是现在基数 base 已经确定是二进制了,就不用在内存中体现出来了,这样只需要在内存中存储符号 sign、尾数 mantissa、指数 exponent 这三个不确定的元素就可以了。

仍然以 19.625 为例,将它转换成二进制形式的浮点数格式:

19.625 = 1.0011101×2^4

此时符号 sign 为 0,尾数 mantissa 为 1.0011101,指数 exponent 为 4。

1) 符号的存储

符号,单独分配出一个位(Bit)来,用 0 表示正数,用 1 表示负数。对于 19.625,这一位的值是 0。

2) 尾数的存储

当采用二进制形式后,尾数部分的取值范围为 1 ≤ mantissa < 2,这意味着:尾数的整数部分一定为 1,是一个恒定的值,这样就无需在内存中提现出来,可以将其直接截掉,只要把小数点后面的二进制数字放入内存中即可。对于 1.0011101,就是把 0011101 放入内存。

我们不妨将真实的尾数命名为 mantissa,将内存中存储的尾数命名为 mant,那么它们之间的关系为:

mantissa = 1.mant

如果 base 采用其它进制,那么尾数的整数部分就不是固定的,它有多种取值的可能,以十进制为例,尾数的整数部分可能是 1~9 之间的任何一个值,这样一来尾数的整数部分就不能省略了,必须在内存中体现出来。而将 base 设置为二进制就可以节省掉一个位(Bit)的内存,这也算是采用二进制的一点点优势。

3) 指数的存储

指数是一个整数,并且有正负之分,不但需要存储它的值,还得能区分出正负号来。

所以指数用的是中经指数偏差(exponent bias)处理后的指数,即用要用上这个中经指数偏差的值加上二进制指数的大小的和去存到内存中。例如19.625 转换后的指数为 4,4+127 = 131,131 换算成二进制为 1000 0011,这就是 19.626 的指数部分在 float 中的最终存储形式。

所以在float中的19.626在内存中的存储是0 10000011 00111010000000000000000,如果是double在内存中的存储为0 10000000011 0011101000000000000000000000000000000000000000000000‬‬

4)中经指数偏差

中经指数偏差也就是上面的127,float 的指数部分占用 8 Bits,能表示从 0~255 的值,取其中间值 127,中间值的求取有固定的公式。设中间值为 median,指数部分占用的内存为 n 位,那么中间值为:

median = 2^(n-1) - 1

对于 float,中间值为 2^(8-1) - 1 = 127;对于 double,中间值为 2^(11-1) -1 = 1023。

我们不妨将真实的指数命名为 exponent,将内存中存储的指数命名为 exp,那么它们之间的关系为:

exponent = exp - median

也可以写作:

exp = exponent + median

二进制浮点数分配内存

C语言中常用的浮点数类型为 float 和 double;float 始终占用 4 个字节,double 始终占用 8 个字节。

下图演示了 float 和 double 的存储格式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFKHCSV7-1580913591327)(img/19.png)]

大端与小端

计算机系统中含有两种存放数据的字节序:大端(Big-endian)和小端(Little-endian)。

大端字节序是指在读写一个大于1个字节的数据时,数据的最高字节存放在起始地址单元处,数据的最低字节存放在最高地址单元处。

小端字节序是指在读写一个大于1个字节的数据时,其数据的最低字节存放在起始地址单元处,数据的最高字节存放在最高地址单元处。

也就是说大端的是高字节放在低地址处,低字节放在高地址处,记忆:大(大端)的让小的(低地址),

小端是低字节放在低地址处,高字节放高地址处,记忆:小(小端)的找小的(低地址)

⽐如,我们要在地址0x00001000处存放⼀个0x04030201的32位整数,其⼤端、⼩端存放情况如图

[外链图片转存中...(img-fFKHCSV7-1580913591327)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值