当你迷茫的时候,请点击 目录大纲 快速查看技术文章,相信你总能找到前行的方向
前情提要
上文 速学C/C++启蒙 我们讲了一个新版本的取经故事,里面暗含编程的基础知识,变量与内存,本文我们用硬核的技术来解读。
有多硬核呢?手搓内存
的那种……
内存解密
内存像什么?
像我家的一亩三分地
,我可以给它尽情划分,这块种西瓜
,这块种黄瓜
,那块种青菜
,那块地种辣椒
……
最像的是那种 LED 显示屏
,很多 LED 点阵的那种,每个点就是一个 bit 位,最简单的就是那种只有亮与不亮的那种。
我们自己也可以 diy 出一个巨形LED内存条
,每行只有8个灯(1Byte)
,当有1024行
(1K)时,我们就有了1KB
的内存,1024*8=8192
个 LED 小灯,也就是1KB=8192bit
,这是要斥巨资搞啊!
如果我再重复搞10个1KB
的内存块,就是10KB
了
同理,我如果来 1024个1KB
的内存块,就是1024KB=1MB
再来1024个1MB
的内存块就是1GB
,我们来算一笔账:
1GB=1024MB=1024*1024KB=1024*1024*1024B=1024*1024*1024*8bit = 需要 8589934592 个 LED 灯
,85 亿
个 LED,看起来用 LED 做内存成本还是太高了哈!
现在哪个内存不是几十个G
起步,所以内存贵的不是没有道理的,你要想到里面有几百亿个灯
呢。
1KB 内存玩法
由于预算不足,我就弄个1KB的内存
玩玩吧(算了,还是用画的吧),很多单片机最开始内存也是很小的,但也是可以玩的。
试试看存一些东西,看得出来我写了什么吗?
ILY = I Love You
,你如果想表白一个程序员女生,可以试着给她发这样的一串暗语:
0x00A5
0x00A2
0x00BA
你说她会不会看懂你的意思呢?看不懂就直接明说吧,没啥大不了的
不要拿幸福作赌注,宁可出点错也千万不要错过!
模拟运行
就这样,我们1KB的LED内存条
就算打造好了哈!还能做表白神器
,而且这些灯既是小灯又是开关
,一按下去它就亮起来了。我给每一行都编了个号码,从 0 开始到 1023
结束,用十六进制表示为:0x0000 ~ 0x03ff
,总共 1024
行(Byte
),8192
个灯(bit
)。
没有 cpu
,我们手搓不了,就拿自己当 cpu
吧,可以读取内存
(采用瞪眼法
)和操作内存
(手动按灯
)。
好了,一台人形计算机
就搞出来了,我真是个天才!天才第一步,先算1+2=?
简单吧,陈景润老师双手表示赞成
,算了,我们挑战一下低级版的1+2
吧,没错,就是小学生算的那个……
由于瞪眼法
,易得1+2=3
说到瞪眼法
,就不得不提拉马努金
了,老师哈代
问他,什么是瞪眼法啊?
“瞪眼法就是把眼睛睁开,瞪着问题看,等下答案就出来了……”
哦,这样啊?那我也试试,但我瞪出来的结果是眼药水
,这位印度的瞪眼大神
,简直是个行走的公式发明机
,可惜英年早逝
,再给他点时间说不定就能把 1+1
给瞪出来了
那我们用人形计算机算一下,类似笔算
在 10 号内存块
(十六进制的地址为 0x000a
= 十进制为 10)放置一个 1
,怎么表示呢,有 8 个 led 灯
,我可以亮 1 个灯表示 1,亮 8 个灯表示 8,也可以,但是最大只能表示 8 了
很浪费!正确的做法用二进制,0b00000001
表示 1
,0b00000010
表示 2
,0b11111111
表示 255
,即:
#1 = 0x0001 = 0b00000001 = 2^0
#2 = 0x0002 = 0b00000010 = 2^1
#3 = 0x0003 = 0b00000011 = 2^1+2^0
……
#255 = 0x00ff = 0b11111111 = 2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0
哇,小小的 8 个灯
,一下子可以表示 255
个数,是不是很神奇!比之前只能表示 8 提高了 32 倍之多!不得不佩服,人才!我们伏羲八卦图
就含有二进制的思路,二进制
也赋予了现代计算机的灵魂。
那我就手动编程
,在10 号位置
对着最后一个灯按了下去,那个灯就亮了,
也就是在 10 号位置
(地址 0x000a
)放一个 1(0b00000001
),说人话就是 10 号位置有个 1
,记为 a=1
;
这个 a 就是个变量
,1 就是数值
,这是给人看的助记
,然而编译器编译后
给机器看的就大概就是,地址:数值
,如下所示:
0x000a : 0b00000001
那趁热再来一个,在 11 号位置
(地址 0x000b
)放一个 2(0b00000010
),记为 b=2
,
0x000b : 0b00000010
之后就进入到 cpu 运算
环节了,
- 用瞪眼法看到 10 号位的值,哦,是 1
- 用瞪眼法看到 11 号位的值,哦,是 2
- 用人形 cpu(脑子)计算 1+2,得到 3
- 把结果往 12 号位置放一下,记为 c = a + b,也就是 3,我赶紧手动编程,等下忘记了,
0x000c:0b00000011
整个用到的内存图就是这样了:
计算完毕,完美!
那如果再复杂一点,算一下平方吧,
(1+2)*(1+2)=?
前面步骤一样,计算左边的(1+2)
得 3,二进制值为 0b00000011
,存入 12 号位置 c
,即 0x000c:0b00000011
再计算右边的(1+2)
得 3,二进制值为 0b00000011
,存入 13 号位置 d,即
0x000d:0b00000011
最后计算 c*d
,也就是 3*3
,我小学的 DNA 告诉我,三三得 9
,用二进制表示为 0b00001001
,再把 9 存到一个位置,就存到 14 号位置 e
,即
0x000e:0b00001001
再来看看现在内存的全貌:
总共 1KB 的内存
,我们在运行的时候用于存储阶段性的成果
,也就是变量数值
,用了 5B
,厉害吧?
发现没有?内存就像是我们小时候的草稿纸
,首先拆分计算的步骤,每计算一步就记录
下来(写内存
),计算下一步就看看上步的结果
(读内存
),最后再计算得出结果(CPU 运算
),那些变量
就像是解方程设的未知数 x,y
,果然,技术源于生活啊!
还是那句话,自己动手,丰衣足食
,多动动手动动脑,以最原始的状态去设计(从无到有),多以计算机的角度
去体验,你就更能明白和理解
计算机,它为什么这样设计和运行。
在上面的人形计算机
,我还简化了计算机取指令
的步骤,只记录了变量,那些加减乘除的符号呢?计算机肯定也是要记录的,这就是代码方法区
了,在 jvm 会存储到元空间内存,为什么又是内存?因为要与CPU打交道
,如果存磁盘,那得慢成一坨翔了!
变量
变量在这起到什么作用呢? 我们上面说过了,助记符,对应内存地址,不过我觉得变量的重要作用是可以最大限度的复用
内存
怎么说呢?其实在上面 (1+2)*(1+2)=?
我就偷偷地用了变量的复用,不知道你发现没有?来回顾一下:
我在计算左边的(1+2)
用到了 10,11,12号内存
,分别存在10号内存了1(a=1)
, 在11号内存存了2(b=2)
在12号内存存了3(c=a+b=1+2=3)
,可以解读为10号内存值
与11号内存值
加和的结果存放到12号内存
。
左边这里都没有什么问题,内存都是必须要用的,用了3B,而且省不了一点点,那我再计算右边的(1+2)
时候,我用了3B吗?没有,我用了1B,因为1,2
这两个值在计算左边的时候已经存到了10,11 号内存
,那计算右边的(1+2)
我只需要把10,11 号内存
的数值取出来加和运算得到3,存入到13号内存,即d=a+b=1+2=3
不复用变量的情况
,即来一个数字就用占一块内存,那这个示例(1+2)*(1+2)=?
有4个明确的数字 1,2,1,2,那我用10, 11, 20, 21号内存
分别存放,用伪代码来展示一下:
int a=1; // 10号内存放入1
int b=2; // 11号内存放入2
int c=a+b; // 12号内存放入计算结果3
int x=1; // 再来一块内存,假如是20号内存放入1
int y=2; // 再来一块内存,假如是21号内存放入2
int d=x+y; // 13号内存放入计算结果3
int e=c*d; // 14号内存放入计算结果9
这里我们简单理解每一个不同的变量对应着一块内存存放数据,那这里面我们用到了 a,b,c,x,y,d,e
7块内存,占我LED内存条7B
(我设定每个数用1Byte=8bit
存储)
- 那
复用变量 a=1,b=2
后就变成了:
int a=1; // 10号内存放入1
int b=2; // 11号内存放入2
int c=a+b; // 12号内存放入计算结果3
// 复用变量a,b
int d=a+b; // 13号内存放入计算结果3
int e=c*d; // 14号内存放入计算结果9
用到了 a,b,c,d,e
5块内存,占我LED内存条5B
,少了2B
,不明显是吧,那我换成百分比皮一下,减少了28.6%
呢?
还可以继续优化,左边和右边(1+2)
结果不都是一样的吗?那我直接复用结果c
呗
复用变量 c
就变成了:
int a=1; // 10号内存放入1
int b=2; // 11号内存放入2
int c=a+b; // 12号内存放入计算结果3
// 复用变量c
int e=c*c; // 14号内存放入计算结果9
用到了 a,b,c,e
4块内存,占我LED内存条4B
,比最开始的7B少了3B
,换成百分比就减少了42.9%
呢,厉不厉害,内存一下子减半了,哈哈!
虽然有点无语,这不就少个1B 2B
的事情吗?但我这只是用个小示例模拟,而且一个数据占的可不是1B甚至会更大,而现实生产上有大量的方法计算
,有大量的变量和内存占用,我们有的java项目没有1个G都启动不起来
,那你估计一下有多少变量占据着内存?所以,变量的复用确实能起到非常明显的效果,可不要小瞧它哦!
总结
我们来稍微总结一下,列个表格来类比它们的关系
术语 | 饰演者 | 备注 |
---|---|---|
CPU | 你 | 做加减乘除之类的运算,读写内存 |
内存 | 草稿 | 记录每一步计算结果 |
读取内存 | 眼睛 | 采用瞪眼法,查看内存里面的数据 |
写内存 | 手 | 用手动按灯(手动编程),点亮对应位置的灯 |
变量 | 未知数x,y | 用来代表某个地址,CPU会根据地址取出内存值,给人感觉就像是数值存到了变量里面 |
后面内容会越来越精彩,走过路过,千万不要错过,不求赞助,但求请点赞收藏加关注
……