代码变成指令后,是一条一条顺序执行的。
在代码的执行过程中,CPU 中的寄存器扮演着举足轻重的角色。
在硬件上,寄存器有锁存器或触发器组成。

每种寄存器有不同的功能,而且每种寄存器都在指令执行过程中扮演重要角色。
- PC 寄存器(指令地址寄存器):用来存放下一条需要执行的计算机指令的 内存地址。
- 指令寄存器:用来存放正在执行的指令。
- 条件码寄存器:用里面的一个个标志位,用来存在 CPU 进行计算或逻辑运算的结果。
以下图片展示如何根据机器码执行指令;

我们可以看到,执行程序时,CPU 会根据 PC 寄存器中的内存地址,将指定内存的指令码读取到指令寄存器中,指令长度自增,开始顺序执行下一个指令,执行上面相同的操作。
以上可见程序的指令码是在内存中顺序保存的,也会一条一条加载。
指令码跳转执行
程序的指令码在内存中是顺序执行的,那么在程序执行跳转逻辑是,机器指令是如何执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // test.c #include <time.h> #include <stdlib.h> int main() { srand(time(NULL)); int r = rand() % 2; int a = 10; if (r == 0) { a = 1; } else { a = 2; }
|
编译命令:
1 2
| gcc -g -c test.c objdump -d -M intel -S test.o
|
在 Mac 上对应的汇编码如下,限于平台问题,和原博客有所差别,所以直接参看原博客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 1 // test.c 26: 99 cltd 27: b9 02 00 00 00 movl $2, %ecx 2c: f7 f9 idivl %ecx 2e: 89 55 f8 movl %edx, -8(%rbp) ; int a = 10; 31: c7 45 f4 0a 00 00 00 movl $10, -12(%rbp) ; if (r == 0) 38: 83 7d f8 00 cmpl $0, -8(%rbp) 3c: 0f 85 0c 00 00 00 jne 12 <_main+0x4e> ; a = 1; 42: c7 45 f4 01 00 00 00 movl $1, -12(%rbp) ; } else { 49: e9 07 00 00 00 jmp 7 <_main+0x55> ; a = 2; 4e: c7 45 f4 02 00 00 00 movl $2, -12(%rbp) ; } 55: 8b 45 fc movl -4(%rbp), %eax 58: 48 83 c4 10 addq $16, %rsp 5c: 5d popq %rbp 5d: c3 retq
|
原博客汇编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if (r == 0) 3b: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0 3f: 75 09 jne 4a <main+0x4a> { a = 1; 41: c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1 48: eb 07 jmp 51 <main+0x51> } else { a = 2; 4a: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2 51: b8 00 00 00 00 mov eax,0x0 }
|
重点关注以下两行(r == 0 的编译结果):
1 2
| 3b: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0 3f: 75 09 jne 4a <main+0x4a>
|
判断当条件成立时,跳转到操作数为 4a 的指令,进行接下来的指令执行。
执行过程图:

所以代码变成指令后,是一条一条顺序执行的,这个顺序执行是基于跳转的顺序执行。
常见指令解释:
cmp DWORD PTR [rbp-0x4],0x0
从寄存器地址为[rbp-0x4]取出值与 0x0 比较
mov DWORD PTR [rbp-0x8],0x2
将 0x2 存入寄存器地址为 [rbp-0x8] 的内存中
jne 指令,是 jump if not equal