从计算机电路来理解指针

我学习 C++ 遇到的第一个难以理解的点就是指针,一级指针多级指针搞得晕头转向,为了理解指针,早些年看了很多文章也看过许多视频。当时我都是这么类比的:变量就是我的名字,指针就是我的住址,引用就是我的别名,二级指针是住址的住址,三级指针那就是住址的住址的住址,再往后以此往下类推。

1. 类比法理解

int main() {
    // 定义变量
    int number = 1;
    // 获取指针
    int* p_number = &number;
    // 通过指针操作变量赋值
    *p_number = 2;
    // cout << "number = " << number << endl;
    return 0;
}

以上是一个最简单的代码,number 就是变量的名字,p_number 就是通过变量拿到了住址,*p_number 通过住址找到变量,可以间接的操作变量赋值 。这样理解几乎能解决所有开发中遇到的问题,除非特别复杂奇怪的场景。那后面 new 对象出来的指针呢?其实本质上也都是一样的。

2. 从计算机电路来理解指针

所谓类比,只是方便大家记忆,其实就是说完全不是这么一回事。在计算机看来,变量、指针、住址、别名等等这些,都是不存在的,唯一存在的就是内存高低电压(地址)。接下来,我们试着从计算机电路的角度来理解指针。

汇编结果.png

这图是我们上面的代码编译出来的结果,红色标记的是 cpu 最终要执行的机器码指令,这里是以 16 进制来呈现的,最终编译出来是 101101...... 这样。蓝色标记(希望我没色盲)的是汇编指令,这个是编译的中间产物代码,为了方便我们理解所以我把汇编指令也放出来了。汇编和机器码指令这些不在本文详细解释,不是我们本文的重点,后面我会陆续写一些文章。写文章实在是太耗时间了,一篇文章几乎花掉我一周的业余时间,但视频我可能 10 分钟就能讲清楚。这里我简单用文字解释,大家感兴趣可以去这个网站实践代码:compiler explorer

int number = 1; 
// 对应如下
mov    DWORD PTR [rbp-0xc],0x1

int* p_number = &number;
// 对应如下
lea    rax,[rbp-0xc]
mov    QWORD PTR [rbp-0x8],rax

*p_number = 2;
// 对应如下
mov    rax,QWORD PTR [rbp-0x8]
mov    DWORD PTR [rax],0x2
  • mov DWORD PTR [rbp-0xc],0x1:cpu 读到这条指令将向内存地址 [rbp-0xc] 写入 0x1
  • lea rax,[rbp-0xc]:cpu 读到这条指令将 [rbp-0xc] 的地址值赋值给 rax 寄存器
  • mov QWORD PTR [rbp-0x8],rax:cpu 读到这条指令将 rax 寄存器的值写入 [rbp-0x8] 内存地址
  • mov rax,QWORD PTR [rbp-0x8]:cpu 读到这条指令将 [rbp-0x8] 的值赋值给寄存器 rax
  • mov DWORD PTR [rax],0x2:cpu 读到这条指令将向 rax 内存地址上存的值当作目标地址,写入 0x2,注意这里的 rax 用 [ ] 围起来了

2.1 cup 操作内存地址

cpu 操作内存简图.png
  • cpu 通过地址总线可以选择操作内存条上的某个位置,早期 cpu 只有 20 根地址总线内存只有 1M,现在一般都有 32 根地址线那么就可以达到 4GB。这个就是我们本文所说的内存地址
  • cpu 通过读写操作线可以选择是读入数据还是写入数据
  • cpu 通过数据总线把数据写入内存条,也可以从内存中把数据读入寄存器,32 根线代表我们一次能读取 32 位数据,64 根代表我们一次能读取 64 位数据,也就是我们常说的 32 位计算机与 64 位计算机

现在我们可以来尝试着翻译一下 cup 是如何操作内存地址的了,注意此处省略 cpu 读取指令的操作等等细节,对应代码如下:

int* p_number = &number;

翻译成汇编代码如下:

lea    rax,[rbp-0xc]
mov    QWORD PTR [rbp-0x8],rax

翻译成 cpu 机器指令如下:

01001000100011010100100111110100
01001000100010010100100111111000

假设我们现在 rbp 寄存器的值存的是 0x0f0f0f0d(32位计算机)第一条指令不需要操作内存,把寄存器 rbp 的值减掉 12 存到 rax 寄存器上,执行完第一条指令后,寄存器 rax = rbp - 12 = 0x0f0f0f01。第二条指令需要操作内存,把 rax(0x0f0f0f01) 的值写入到 0x0f0f0f0f5 的内存地址上。将上图的读写操作线设置为写,往读写操作线上传输高低电压来控制读写。往地址总线上传输高低电压来选择地址,32 根地址总线上传输高低电压为 00001111000011110000111100001001(0x0f0f0f0f5)往 32 根数据线上传输高低电压为 00001111000011110000111100000001(0x0f0f0f01)执行完当前指令后,以此类推再去读取下一条指令,继续执行代码。

2.2 cup 内部构造

cpu 芯片的内部构造.png
  • 寄存器:像上面的 rax、rbp 代表的都是 cpu 内部的寄存器,这些电路用来临时存放数据,有记忆功能
  • 计数器:从哪里开始取指令,取完第一条指令执行完就要去取下一条指令,有计数功能
  • 计算器:有些指令需要做加减乘除,有计算功能
  • 控制器:读取指令译码,操作执行指令,控制内存、IO 等,有控制功能

无论多复杂的 CPU 内部都是由电子元器件(主要是晶体管)组成的数字电路,寄存器电路最简单,控制器电路最复杂。这些不在本文的范围内,我有打算后面录制一些科普视频,从电子元器件 -> 数字电路(门电路) -> CPU 内部的详细电路。关于 CPU 芯片卡脖子的制造工艺和历史,我推荐我们 WXG 的微信读书 App 里面的《芯片战争》

3. 最后杂谈

高级语言 -> 汇编语言 -> cpu工作原理 -> 数字电路,再加上数据结构算法、编译原理、操作系统和虚拟机。这个是我的一个学习路径,仅供大家参考。知识浩瀚无限,人的精力和时间却有限,仅仅是一个 linux 操作系统就有上千万行代码,所以我也是一无所知。原则上我们需要尽可能熟悉我们工作的下一层原理,比如以前我做 Android 的时候会花尽可能多的精力去阅读 Framework 的源码,所以之前对于 Android 的分享到 Framework 就打止了。我现在做 iOS 和 C++ 需要了解的就更多了一些。

现代计算机很复杂,我上面画的图非常简单,比如 CPU 内部现在一般都是多核、指令执行有指令流水线、cpu 内部还有多级的快速缓存。我们移动端开发的应用都是运行在操作系统上的,对于我们应用层来说都是虚拟地址,操作系统加上硬件一起配合才会转成真实的物理地址。早期的计算机并没有这么复杂,更有利于大家学习,所以推荐大家先去了解本质,从简单上手再到复杂。

有些语言看似很复杂,但只要是跑在计算机上,那么本质都是一样的。汇编、c/c++、OC、swift 等等只是语法可能有点不一样,但是最终本质大家都是一样的。有些语言依赖虚拟机比如 Java 语言,但 Java/Android 虚拟机是 C/C++ 参杂汇编写的。有些语言依赖解释器比如 python,解释器也 是 C/C++ 参杂汇编写的。学习一门语言的语法其实不用花太多时间,我记得之前从 Android 转到 iOS 开发也就用了一周的时间,关键还是我们对于底层原理的熟悉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容