寫好的代碼變成了指令以後、是一條條順序執行的就能夠了程序員
N 個觸發器或者鎖存器,就能夠組成一個 N 位(Bit)的寄存器,可以保存 N 位的數據。比方說,咱們用的 64 位 Intel 服務器,寄存器就是 64 位的。
sass
一、CPU會根據PC寄存器裏的地址,從內存裏面把須要執行的指令讀取到指令寄存器裏面直面執行bash
二、而後根據指令長度自增、開始順序讀取下一條指令。能夠看到一個程序的一條條指令在內存裏面是連續保存的。也會一條條順序加載服務器
三、而有些特殊指令(J類跳轉指令)、會修改寄存器裏面的地址oop
四、這樣下一條要執行的指令就不是從內存裏面順序加載的3d
五、事實上、這些跳轉指令存在,也就是咱們在寫程序的時候,使用了 if…else 條件語句和 while/for 循環語句的緣由code
[root@luoahong c]# cat 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; }
咱們用 rand 生成了一個隨機數 r,r 要麼是 0,要麼是 1。當 r 是 0 的時候,咱們把以前定義的變量 a 設成 1,否則就設成 2。orm
[root@luoahong c]# gcc -g -c test.c test.c: In function ‘main’: test.c:15:3: error: expected declaration or statement at end of input } ^ [root@luoahong c]# cat -n test.c 1 #include <time.h> 2 #include <stdlib.h> 3 4 5 int main() 6 { 7 srand(time(NULL)); 8 int r = rand() % 2; 9 int a = 10; 10 if (r == 0) 11 { 12 a = 1; 13 } else { 14 a = 2; 15 }
執行報錯,是由於少了一個}blog
[root@luoahong c]# cat 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; } } [root@luoahong c]# gcc -g -c test.c [root@luoahong c]# objdump -d -M intel -S test.o test.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: #include <time.h> #include <stdlib.h> int main() { 0: 55 push rbp 1: 48 89 e5 mov rbp,rsp 4: 48 83 ec 10 sub rsp,0x10 srand(time(NULL)); 8: bf 00 00 00 00 mov edi,0x0 d: e8 00 00 00 00 call 12 <main+0x12> 12: 89 c7 mov edi,eax 14: e8 00 00 00 00 call 19 <main+0x19> int r = rand() % 2; 19: e8 00 00 00 00 call 1e <main+0x1e> 1e: 99 cdq 1f: c1 ea 1f shr edx,0x1f 22: 01 d0 add eax,edx 24: 83 e0 01 and eax,0x1 27: 29 d0 sub eax,edx 29: 89 45 fc mov DWORD PTR [rbp-0x4],eax int a = 10; 2c: c7 45 f8 0a 00 00 00 mov DWORD PTR [rbp-0x8],0xa if (r == 0) 33: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0 37: 75 09 jne 42 <main+0x42> { a = 1; 39: c7 45 f8 01 00 00 00 mov DWORD PTR [rbp-0x8],0x1 40: eb 07 jmp 49 <main+0x49> } else { a = 2; 42: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2 } } 49: c9 leave 4a: c3 ret
能夠看到,這裏對於 r == 0 的條件判斷,被編譯成了cmp 和 jne 這兩條指令。cmp 指令比較了先後兩個操做數的值,這裏的 DWORD PTR 表明操做的數據類型是 32 位的整數內存
而 [rbp-0x4] 則是一個寄存器的地址。因此,第一個操做數就是從寄存器裏拿到的變量 r 的值。第二個操做數 0x0 就是咱們設定的常量 0 的 16 進製表示。cmp 指令的比較結果,會存入到條件碼寄存器當中去。
在這裏,若是比較的結果是 True,也就是 r == 0,就把零標誌條件碼(對應的條件碼是 ZF,Zero Flag)設置爲 1。除了零標誌以外,Intel 的 CPU 下還有進位標誌(CF,Carry Flah)
符號標誌(SF,Sign Flag)以及溢出標誌(OF,Overflow Flag),用在不一樣的判斷條件下。
cmp 指令執行完成以後,PC 寄存器會自動自增,開始執行下一條 jne 的指令。
[root@luoahong c]# cat test.c int main() { int a = 0; for (int i = 0; i < 3; i++) { a += i; } } [root@luoahong c]# gcc -g -c test.c test.c: In function ‘main’: test.c:4:5: error: ‘for’ loop initial declarations are only allowed in C99 mode for (int i = 0; i < 3; i++) ^ test.c:4:5: note: use option -std=c99 or -std=gnu99 to compile your code
緣由:這是由於gcc是基於c89標準,不能直接在for循環中初始化增量。而C99標準能夠在for循環內定義變量。
咱們再看一段簡單的利用 for 循環的程序。咱們循環自增變量i 三次,三次以後,i>=3,就會跳出循環。整個程序,對應的 Intel 彙編代碼就是這樣的:
[root@luoahong c]# objdump -d -M intel -S test.o test.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <main>: int main() { 0: 55 push rbp 1: 48 89 e5 mov rbp,rsp int a = 0; 4: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 int i; for (i = 0; i < 3; i++) b: c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0 12: eb 0a jmp 1e <main+0x1e> { a += i; 14: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] 17: 01 45 fc add DWORD PTR [rbp-0x4],eax for (i = 0; i < 3; i++) 1a: 83 45 f8 01 add DWORD PTR [rbp-0x8],0x1 1e: 83 7d f8 02 cmp DWORD PTR [rbp-0x8],0x2 22: 7e f0 jle 14 <main+0x14> } } 24: 5d pop rbp 25: c3 ret
能夠看到,對應的循環也是用 1e 這個地址上的 cmp 比較指令,和緊接着的 jle 條件跳轉指令來、實現的。主要的差異在於,這裏的 jle 跳轉的地址,在這條指令以前的地址 14,而非 if…else 編
譯出來的跳轉指令以後。往前跳轉使得條件知足的時候,PC 寄存器會把指令地址設置到以前執行過的指令位置,從新執行以前執行過的指令,直到條件不知足,順序往下執行 jle 以後的指令,整個循環才結束。
其實,你有沒有以爲,jle和jmp指令,有點像邏輯程序裏面的goto命令,直接指定了一個特定條件下的跳轉位置