汇编
通常,CPU会先将内存中的数据存储到寄存器中,然后再对寄存器中的数据进行运算。假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间
markdown
1. CPU首先会将红色内存空间的值放到rax寄存器中:movq 红色内存空间, `%rax`
2. 然后让rax寄存器与1相加:`addq $0x1, %rax`
3. 最后将值赋值给内存空间:`movq %rax`, 蓝色内存空间.如下图1
2
3
2
3

编程语言的发展从机器语言到汇编语言到高级语言,如下图: 
markdown
1. 汇编语言与机器语言一一对应,每一条机器指令都有与之对应的汇编指令
2. 汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言
3. 高级语言可以通过编译得到汇编语言\机器语言,但汇编语言\机器语言几乎不可能还原成高级语言1
2
3
2
3
编程语言分类
汇编语言的种类有 8086汇编(16bit)、x86汇编(32bit)、x64汇编(64bit)、ARM汇编(嵌入式、移动设备)...等等
x86、x64汇编根据编译器的不同,有2种书写格式 Intel:Windows派系、AT&T :Unix派系。iOS开发中最主要的汇编语言是:AT&T汇编 -> iOS模拟器、ARM汇编 -> iOS真机设备。
常见汇编指令

AT&T和Intel的区别:
markdown
* `AT&T`指令前都有%
* `AT&T`mov指令是将左边的值赋值给右边、`Intel` mov指令是将右边的值赋值给左边
* `movq -0x18(%rbp), %rax` 是取出 rbp - 0x18 地址中的值赋值给rax
* `leaq -0x18(%rbp), %rax` 是将 rbp - 0x18 的地址值赋给rax
* `jmp 0x100086` 跳到对应的函数地址,允许函数地址是变化的
* `call 0x100086` 跳到对应的函数地址,然后逐步运行指令,返回跳转前的地址,一般配合ret指令
* e开头的寄存器是32位,比如`eax`
* r开头的寄存器是64位,比如`rax`,可以看下面`AT&T`寄存器的图1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
寄存器
在AT&T中有16个常用寄存器:
markdown
prax、rbx、rcx 、rdx、rsi、rdi、rbp、rsp
pr8、r9、r10、r11、r12、r13、r14、r151
2
2
寄存器的具体用途:
markdown
* rax、rdx常作为函数返回值使用
* rdi、rsi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
* rsp、rbp用于栈操作
* rip作为指令指针存储着CPU下一条要执行的指令的地址,一旦CPU读取一条指令,rip会自动指向下一条指令(存储下一条指令的地址)1
2
3
4
2
3
4

内存地址规律
markdown
* 内存地址格式为:`0x4bdc(%rip)`,一般是全局变量,全局区(数据段)
* 内存地址格式为:`-0x78(%rbp)`,一般是局部变量,栈空间
* 内存地址格式为:`0x10(%rax)`,一般是堆空间1
2
3
2
3
Swift代码示例
swift
let a = 10
print(Mems.ptr(ofRef: a))1
2
2
这是一个简单的赋值操作,使用的是M1芯片的Mac电脑。汇编语法可能会有差异,使用Debug -> Debug Workflow -> Always Show Disassembly查看汇编。使用LLDB指令读取出寄存器的值,根据汇编的含义加上偏移量等于a的地址值。通过查看地址看到确实有a存储在里面。 