第一章 对于程序员来说CPU是什么
1.1 CPU内部结构解析
程序是计算机进行每一步动作的一组指令,程序由指令+数据组成。
机器语言是CPU可以直接识别并且能够直接使用的语言,由二进制代码0和1组成。
正在运行的程序被存储在内存中,表示命令和数据存储位置的数值。
CPU是由运算器(运算从内存读入寄存器的值)、控制器(把内存中的指令和数据读入寄存器,并根据指令的结果控制整个计算机)、时钟(负责CPU开始计时的时钟信号--单位赫兹,Hz,频率越高,CPU运行速度越快)和寄存器(暂存指令、数据等处理对象)这四部分组成。
内存负责存储指令+数据,DRAM(dynamic random access memory)动态随机存储器。其特点是运行速度快,断电数据丢失。
程序的运行机制:程序运行后,根据时钟信号,控制器会将内存中的指令和数据读入到寄存器中,运算器负责运算这些读入到寄存器中的数据,控制器根据运算器所得的结果进而控制计算机。
1.2 CPU是寄存器的集合体
我们通过1.1中对程序的运行机制的了解,我们会发现程序的运行过程其实就是和寄存器不断打交道的过程,因此也就不难理解CPU就是寄存器的集合体这句话了。我们根据寄存器的不同功能,大致可以将寄存器分为八大类别:CPU内只有1个的-->累加寄存器、标指寄存器、程序计数器、指令寄存器、栈寄存器,有多个的-->基址寄存器、变址寄存器、通用寄存器。此处大家只需了解寄存器大致分为这八类即可,对于这些寄存器的具体作用等到后边具体使用的时候我在详细介绍哈。
上图中白色字体表示CPU中该寄存器只有一个,蓝色字体代表该寄存器有多个。
1.3 决定程序流程的程序计数器
CPU的控制器会参照程序计数器的数值,从相应的内存中读取指令和数据并执行与之对应的程序。
1.4 顺序执行和循环机制
顺序执行时程序计数器每执行一次自动+1,跳转指令时则执行jump指令跳转到指定的地址。
1.5函数的调用机制
在讲解函数调用机制之前,我们先来比较一下 跳转指令&函数调用的不同之处
表面上,两者的实现方式貌似相似——都是根据程序计数器设定的值跳转到指定的地址处执行程序,但是在内存中的实际执行机制上边却又是完全不同,一下举两个简单的小栗子哈
跳转指令:
1 int main()
2 {
3 int a = -5;
4 //下一条指令的地址
5 if (a >= 0)
6 {
7 printf("%d\n",a);
8 }
9 else
10 {
11 a = -a;
12 printf("%d\n",a);
13 }
14 return 0;
15 }
我们不难看出,程序执行的顺序是1--2--3--4--5--11--12--14,在执行跳转指令之前,如果第四行有程序则会先去执行第四行的程序。
而函数执行却与此不同
1 #include <stdio.h>
2 int add(int a,int b)
3 {
4 return a + b;
5 }
6 int main()
7 {
8 int a = 10;
9 int b = 20;
10 int c = add(a,b);
11 printf("%d\n", c);
12 return 0;
13 }
由于main()函数是程序的入口,因此该程序的执行顺序是7-->8-->9-->10-->4-->5-->10-->11-->12-->13
细心的你或许就会发现,跳转指令和函数调用还是有区别的。事实上,在内存中,跳转指令执行的是jump指令,它所要关心的只是所要跳转的地址,而不关心返回的问题。但是函数调用就不一样了,它不仅要关注所要调用的地址,还要有返回的问题,其实,在内存中函数调用执行的是call指令,call指令会先把调用结束后要执行的下一条指令的地址存储在内存的栈里边去,然后根据栈先进后出的特点,函数在调用完毕之后便会从内存中释放掉,也就是所谓的出栈,此时便可以通过函数的出口执行return指令,该指令的作用就是将下一条指令的地址的值设定到程序计数器中。
1.6 通过地址和索引实现数组
在此之前介绍两个寄存器——基址寄存器和变址寄存器,以及进制的表示方法。
基址寄存器:存储数据内存的起始地址;
变址寄存器:存储基址寄存器的相对地址。
进制的表示方法:4位二进制可以表示的范围是 0000~1111 ,转化成十进制所表示的范围就是 0~16 (0 1 2 3 4 5 6 7 8 9 10 A B C D E F),其中A B C D E F这几个字母不区分大小写表示的数值是11 、12、13、14、15、16,因此1个16进制的数由4个2进制数表示。那么,在32位机器上便能够表示的内存地址就是00000000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111,此处为了方便沃恩用十六进制表示:0000 0000 ~ FFFF FFFF。则可以通过这种方法模拟出数组的操作:将1000 0000存入基址寄存器(固定值),将 0000 0000 ~ 0000 FFFF之间的值存入到变址寄存器中,那么基址寄存器+变址寄存器的值便是访问内存的实际地址。
理解了这样的存储方式,也就不难理解为什么我们在访问数组元素的时候第一个元素的下标是0了。
所以,CPU的处理并不是特别的复杂,机器语言的主要类型大致可以分为四类:
- 数据传送指令
- 运算指令
- 跳转指令
- call / return 指令
以上便是第一章的读书笔记和心得体会,由于笔者水平有限,篇幅中难免有纰漏之处,也欢迎各位读者批评指正。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY