上一篇「CPU 提供了什麼」中,咱們瞭解了物理的層面的 CPU,爲咱們提供了什麼。程序員
本篇,咱們介紹下高級語言「C 語言」是如何在物理 CPU 上面跑起來的。segmentfault
C 語言做爲高級語言,爲程序員提供了更友好的表達方式。在我看來,主要是提供瞭如下抽象能力:函數
構建一個良好的示例代碼,能夠很好幫助咱們去理解。
下面的示例裏,咱們能夠看到 變量 和 函數 都用上了。優化
#include "stdio.h" int add (int a, int b) { return a + b; } int main () { int a = 1; int b = 2; int c = add(a, b); printf("a + b = %d\n", c); return 0; }
毫無心外,咱們獲得了指望的 3
。編碼
$ gcc -O0 -g3 -Wall -o simple simple.c $ ./simple a + b = 3
咱們仍是用 objdump
來看看,編譯器生成了什麼代碼:3d
call
指令,參數則是函數對應那一段機器指令的第一個指令地址。$ objdump -M intel -j .text -d simple # 截取其中最重要的部分 000000000040052d <add>: 40052d: 55 push rbp 40052e: 48 89 e5 mov rbp,rsp 400531: 89 7d fc mov DWORD PTR [rbp-0x4],edi 400534: 89 75 f8 mov DWORD PTR [rbp-0x8],esi 400537: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] 40053a: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] 40053d: 01 d0 add eax,edx 40053f: 5d pop rbp 400540: c3 ret 0000000000400541 <main>: 400541: 55 push rbp 400542: 48 89 e5 mov rbp,rsp 400545: 48 83 ec 10 sub rsp,0x10 400549: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1 400550: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2 400557: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8] 40055a: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 40055d: 89 d6 mov esi,edx 40055f: 89 c7 mov edi,eax 400561: e8 c7 ff ff ff call 40052d <add> 400566: 89 45 f4 mov DWORD PTR [rbp-0xc],eax 400569: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc] 40056c: 89 c6 mov esi,eax 40056e: bf 20 06 40 00 mov edi,0x400620 400573: b8 00 00 00 00 mov eax,0x0 400578: e8 93 fe ff ff call 400410 <printf@plt> 40057d: b8 00 00 00 00 mov eax,0x0 400582: c9 leave 400583: c3 ret 400584: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 40058b: 00 00 00 40058e: 66 90 xchg ax,ax
這個恰好和局部變量的做用域關聯起來了:code
這樣的話,只須要棧高度恢復,也就意味着被調用函數的全部的臨時變量,所有失效了。內存
答案是,不必定。
上面咱們是經過 -O0
編譯的,接下來,咱們看下 -O1
編譯生成的機器碼。作用域
此時的局部變量直接放在寄存器裏了,不須要寫入到棧空間了。
不過,此時 main
都已經再也不調用 add
函數了,由於已經被 gcc 內聯優化了。
好吧,構建個合適的用例也不容易。get
000000000040052d <add>: 40052d: 8d 04 37 lea eax,[rdi+rsi*1] 400530: c3 ret 0000000000400531 <main>: 400531: 48 83 ec 08 sub rsp,0x8 400535: be 03 00 00 00 mov esi,0x3 40053a: bf f0 05 40 00 mov edi,0x4005f0 40053f: b8 00 00 00 00 mov eax,0x0 400544: e8 c7 fe ff ff call 400410 <printf@plt> 400549: b8 00 00 00 00 mov eax,0x0 40054e: 48 83 c4 08 add rsp,0x8 400552: c3 ret 400553: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 40055a: 00 00 00 40055d: 0f 1f 00 nop DWORD PTR [rax]
咱們用以下命令,關閉 gcc 的內聯優化:
gcc -fno-inline -O1 -g3 -Wall -o simple simple.c
再來看下彙編代碼,此時的機器碼就符合理想的驗證結果了。
000000000040052d <add>: 40052d: 8d 04 37 lea eax,[rdi+rsi*1] 400530: c3 ret 0000000000400531 <main>: 400531: 48 83 ec 08 sub rsp,0x8 400535: be 02 00 00 00 mov esi,0x2 40053a: bf 01 00 00 00 mov edi,0x1 40053f: e8 e9 ff ff ff call 40052d <add> 400544: 89 c6 mov esi,eax 400546: bf f0 05 40 00 mov edi,0x4005f0 40054b: b8 00 00 00 00 mov eax,0x0 400550: e8 bb fe ff ff call 400410 <printf@plt> 400555: b8 00 00 00 00 mov eax,0x0 40055a: 48 83 c4 08 add rsp,0x8 40055e: c3 ret 40055f: 90 nop
call
指令,意思是接下來跳轉到執行這一段機器指令。