1 编程语言的发展史
1.1 机器语言
计算机的硬件作为一种电路元件,它的输出和输入只能是有电或者没电,也就是所说的高电平和低电平,所以计算机传递的数据是由“0” 和“1”组成的二进制数,所以说二进制的语言是计算机语言的本质。计算机发明之初,人们为了去控制计算机完成自己的任务或者项目,只能去编写“0”、“ 1”这样的二进制数字串去控制电脑,其实就是控制计算机硬件的高低电平或通路开路,这种语言就是机器语言,例如下面的0、1代码就分别代表了四则运算:
加:0100 0000
减:0100 1000
乘:1111 0111 1110 0000
除:1111 0111 1111 0000
直观上看,机器语言十分晦涩难懂,其中的含义往往要通过查表或者手册才能理解, 使用的时候非常痛苦,尤其当你需要修改已经完成的程序时,这种看起来无序的机器语言会让你无从下手,也很难找到程序的错误。而且,不同计算机的运行环境不同,指令方式操作方式也不尽相同,所以当你在这种机器语言就有了特定性,只能在特定的计算机上执行,而一旦换了机器就需要重新编程,这极大的降低了程序的使用和推广效率。但由于机器语言具有特定性,完美适配特定型号的计算机,故而运行效率远远高过其他语言。机器语言,也就是第一代编程语言。
1.2 汇编语言
因此,为了解决以上种种难题,汇编语言就此营运而生,汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。
例如,四则运算在汇编语言中的表现如下形式:
加:INC EAX 对应的机器语言: 0100 0000
减:DEC EAX 对应的机器语言: 0100 1000
乘:MUL EAX 对应的机器语言:1111 0111 1110 0000
除:DIV EAX 对应的机器语言:1111 0111 1111 0000
1.3 高级语言
在编程语言经历了机器语言,汇编语言等更新之后,人们发现了限制程序推广的关键因素——程序的可移植性。需要设计一个能够不依赖于计算机硬件,能够在不同机器上运行的程序。这样可以免去很多编程的重复过程,提高效率,同时这种语言又要接近于数学语言或人的自然语言。在计算机还很稀缺的50年代,诞生了第一个高级编程语言。当时计算机的造价不菲,但是每天的计算量又有限,如何有效的利用计算机有限的计算能力成为了当时人们面对的问题。同时,因为资源的稀缺, 计算机的运行效率也成为了那个年代工程师追寻的目标。为了更高效的使用计算机,人们设计出了高级编程语言,来满足人们对于高效简洁的编程语言的追求。
例如:C\C++\Java\OC\Swift,更加接近人类的自然语言
比如C语言:
加:A+B 通过编译器 0100 0000
减:A-B 通过编译器 0100 1000
乘:A*B 通过编译器 1111 0111 1110 0000
除:A/B 通过编译器 1111 0111 1111 0000
我们的代码在终端设备上是这样的过程:
其中:
- 汇编语言与机器语言一一对应,每一条机器指令都有与其对应的汇编指令。
- 汇编语言可以通过编译得到机器语言,机器语言也可以通过反汇编得到汇编语言。(针对特定平台上的机器指令所对应的汇编指令)
- 高级语言可以通过编译得到汇编\机器语言,但是汇编\机器语言几乎不可能还原成高级语言。(这是因为不同的高级语言编译后产生的汇编语言可以相同,同一种高级语言不同的代码段产生的汇编语言也可以相同,这就造成了汇编语言反编译成高级语言的困难。)
2 汇编语言的特点、用途及种类
2.1 特点
- 可以直接访问、控制各种硬件设备,比如存储器、CPU等,能最大限度地发挥硬件的功能。
- 能够不受编译器的限制,对生成的二进制代码进行完全的控制。
- 目标代码简短,占用内存少,执行速度快。
- 汇编指令是机器指令的助记符,同机器指令一一对应。每一种CPU都有自己的机器指令集\汇编指令集,所以汇编语言不具备可移植性。
- 知识点过多,开发者需要对CPU等硬件结构有所了解,不易于编程、调试及维护。
- 不区分大小写,比如mov和MOV是一样的。
2.2 用途
- 编写驱动程序、操作系统(比如Linux内核的某些关键部分)。
- 对性能要求极高的程序或者代码片段,可与高级语言混合使用(内联汇编)。
- 软件安全。
- 病毒分析与防治。
- 逆向\加壳\脱壳\破解\外挂\免杀\加密解密\漏洞\黑客。
- 理解计算机系统的最佳起点和最有效途径。
- 为编写高效代码打下基础。
- 弄清代码的本质。
2.3 种类
目前讨论比较多的汇编语言有:
8086汇编(8086处理器是16bit的CPU)
Win32汇编
Win64汇编
ARM汇编(嵌入式、Mac、iOS)
我们iPhone里面用到的是ARM汇编,但是不同的设备也有差异,因CPU的架构不同,如下图所示:
3. 学习汇编语言必备基础知识
3.1 CPU硬件结构
3.2 APP/程序执行过程图
其中硬件相关最为重要的是CPU以及内存,在汇编指令集中,大部分指令都是直接对CPU以及内存进行操作的。
3.3 总线
3.3.1 总线的工作流程
总线其实就是一根根导线的集合,分为:地址总线、数据总线以及控制总线。
当CPU需要从内存中读写数据的时候,通过控制总线传入读写内存数据的操作命令,然后通过地址总线传入的地址读写内存中相应地址的数据,如果是读操作就将获取到的数据通过数据总线传给CPU,如果是写操作就将数据通过数据总线存储到地址总线所记录的内存的地址中,如下图所示:
3.3.2 地址总线
地址总线的宽度决定了CPU的寻址能力,8086的地址总线宽度是20,所以寻址能力是1M( 2的20次方 )。也就是说内存的大小限制决定了地址总线的宽度,地址总线所读取的内存的最大地址不能超过内存存储数据的最大存储总量。
3.3.3 数据总线
数据总线的宽度决定了CPU的单次数据传送量,也就是数据传送速度,8086的数据总线宽度是16,所以单次最大传递2个字节的数据。
3.3.4 控制总线
控制总线的宽度决定了CPU对其他器件的控制能力、能有多少种控制。
3.4 内存
- 内存地址空间的大小受CPU地址总线宽度的限制。8086的地址总线宽度为20,可以定位220个不同的内存单元(内存地址范围0x00000~0xFFFFF),所以8086的内存空间大小为1MB。
- 0x00000~0x9FFFF:主存储器。可读可写。
- 0xA0000~0xBFFFF:向显存中写入数据,这些数据会被显卡输出到显示器。可读可写。
- 0xC0000~0xFFFFF:存储各种硬件\系统信息。只读。
3.5 进制
3.5.1 如何学习进制
在学习会汇编的时候我们需要经常查看指令执行的地址,以及一些二进制数据,因此对于进制的学习也是非常必要的。
我们在学习进制的时候,总是以十进制为依托去考虑其他进制,需要运算的时候也总是先转换为十进制,其实这种学习方法是错误的,每一种进制其实都是完美的,要向学号进制首先要忘掉十进制,也要忘掉进制间的转换。
3.5.2 进制的定义
- 八进制由8个符号组成:0 1 2 3 4 5 6 7 逢八进一
- 十进制由10个符号组成:0 1 2 3 4 5 6 7 8 9 逢十进一
- N进制就是由N个符号组成:逢N进一
其实这些符号不一定是写死的,可以是自己定义的,例如:什么时候1 + 1等于3呢?
当你如此定义十进制的符号就可以做的:0 1 3 6 9 A B Q X 4 传统我们定义的十进制和定义的十进制不一样,那么这10个符号如果我们不告诉别人,别人是无法知道我们这些符号具体的表示意义的,这样就可以用来加密。
3.5.3 进制的运算
- 八进制加法表
1+1 = 2
1+2 = 3 2+2 = 4
1+3 = 4 2+3 = 5 3+3 = 6
1+4 = 5 2+4 = 6 3+4 = 7 4+4 = 10
1+5 = 6 2+5 = 7 3+5 = 10 4+5 = 11 5+5 = 12
1+6 = 7 2+6 = 10 3+6 = 11 4+6 = 12 5+6 = 13 6+6 = 14
1+7 = 10 2+7 = 11 3+7 = 12 4+7 = 13 5+7 = 14 6+7 = 15 7+7 = 16 - 八进制的乘法表
11 = 1
12 = 2 22 = 4
13 = 3 23 = 6 33 = 11
14 = 4 24 = 10 34 = 14 44 = 20
15 = 5 25 = 12 35 = 17 45 = 24 55 = 31
16 = 6 26 = 14 36 = 22 46 = 30 56 = 36 66 = 44
17 = 7 27 = 16 37 = 25 47 = 34 57 = 43 67 = 52 77 = 61
3.5.4 进制的转换
二进制:1 0 1 1 1 0 1 1 1 1 0 0
三个一组转换为八进制:101 110 111 100
八进制: 5 6 7 4
四个一组转换为十六进制:1011 1011 1100
十六进制: b b c
将二进制转换为十六进制写起来就没那么麻烦了,因此,我们经常使用十六形式来简写二进制。
3.6 数据的宽度
数学上的数字,是没有大小限制的,可以无限的大。但在计算机中,由于受硬件的制约,数据都是有长度限制的(我们称为数据宽度),超过最多宽度的数据会被丢弃。
计算机中常见的数据宽度
- 位(Bit):1个位就是1个二进制位,0或者1。
- 字节(Byte):1个字节由8个Bit组成(8位),内存中的最小单元Byte。
- 字(Word):1个字由2个字节组成(16位),这2个字节分别称为高字节和低字节。
- 双字(Doubleword):1个双字由两个字组成(32位)。