《讀書筆記》程序員的自我修養之編譯和連接

《讀書筆記》程序員的自我修養之編譯和連接前端

對於經典的Hello world,程序是如何運行的呢?linux

#include <stdio.h>程序員

int main()算法

{後端

       printf("Hello World\n");函數

       return 0;工具

}優化

對於GCC編譯器,程序運行分爲如下四個過程:spa

預編譯(Prepressing)--à編譯(Compilation)--à彙編(Assembly)--à連接(Linking)翻譯

                    

預編譯

預編譯是將.C文件預編譯成.i文件

$gcc –E hello.c –o hello.i

$cpp hello.c > hello.i

注:‘-E’選項表示只進行預編譯;cpp是預編譯器

 

預編譯過程主要處理源代碼文件中的以「#」開始的預編譯指令(「#include」,「#define」等)。主要處理規則以下:

  • 把全部的宏展開,刪除全部的「#define」;
  • 處理全部的條件編譯(「#if」,「ifdef」,「#elif」,「#else」,「#endif」);
  • 處理「#include」預編譯指令,將被包含的文件插入到該預編譯指令位置;
  • 刪除全部註釋「//」和「/* */」;
  • 添加行號和文件名標識(這就是爲何程序報錯時會顯示錯誤代碼行號);
  • 保留全部編譯命令#pragma(留給編譯器使用)。

編譯

編譯是整個過程的核心,也是最複雜部分之一。包括詞法分析、語法分析、語義分析、源代碼優化,生成彙編代碼。

編譯過程命令以下:

$gcc –S hello.i –o hello.s

目前的GCC版本將預編譯和編譯過程合二爲一,使用一個叫作ccl的程序來完成這兩個過程。

可直接調用ccl來完成預編譯和編譯過程,以下:

$ /user/lib/gcc/i486–linux–gnu/4.1/ccl hello.c

或者

$gcc –S hello.c –o hello.s

彙編

彙編器(as)是將彙編代碼轉變爲機器可執行的指令(彙編指令和機器指令的對照表一一翻譯)

彙編過程以下:

$as hello.s –o hello.o

或者

$gcc –c hello.s –o hello.o

或者

$gcc –c hello.c –o hello.o

 

連接

連接的過程是連接器(ld)將目標文件(.o文件)變爲可執行文件(.exe)的過程。

連接過程是一個複雜的過程,至關複雜!!!

$ld  -static  crt1.o  crti.o  crtbeginT.o  hello.o  -start-group -lgcc -lgcc_eh -lc-end-group  crtend.o  crtn.o

看了上面的命令,爲何要將一大堆的文件連接起來才能夠獲得可執行文件??

且看後面的靜態連接與動態連接篇幅。

 

上面分析了一段程序的執行過程,下面具體看看編譯器到底作了什麼工做。

 

編譯過程通常能夠分爲6步:掃描(詞法分析)、語法分析、語義分析、源代碼優化、代碼生成和目標代碼優化。

一、掃描器(Scanner)的任務就是進行簡單的詞法分析,運用一種相似於有限狀態機(Finite State Machine)的算法將源代碼的字符序列分割成一系列的記號(Token)。詞法分析產生的記號通常有如下幾類:關鍵字、標識符、字面量(包含數字、字符串等)和特殊符號(如加號、等號)。掃描的過程由lex程序完成。

二、語法分析器(Grammar Parser)將對由掃描器產生的記號進行語法分析,從而產生語法樹(Syntax Tree)。簡單來說,由語法分析器生成的語法樹就是以表達式(Expression)爲節點的樹。語法分析的過程由yacc工具完成。

三、語義分析由語義分析器(Semantic Analyzer)來完成。

----編譯器所能分析的語義是靜態語義,與之對應的動態語義只有在運行期才能肯定。

----靜態語義一般包括聲明和類型的匹配,類型的轉換。

----通過語義分析後,語法分析生成的語法樹的表達式都被標識了類型,若是有些類型須要隱式轉換,語義分析程序會在語法樹中插入相應的轉換節點。

四、源代碼優化器對源代碼進行優化,但直接在語法樹上做優化比較困難,每每是將整個語法樹轉換成中間代碼進行優化。

中間代碼使得編譯器分爲前端和後端:前端負責產生機器無關的中間代碼;後端負責將中間代碼轉換成目標機器代碼。

五、目標代碼優化器將上述生成的目標機器代碼進行優化,好比選擇合適的尋址方式、使用位移代替乘法運算、刪除多餘的指令等。

 

編譯器忙活了半天,可生成的目標代碼中,咱們還不知道函數訪問所要的目標函數的地址,變量訪問所要的目標變量的地址,這可咋辦呢??

其實目標函數訪問也好,變量訪問也好,這均可以歸結爲一種方式,就是所謂的模塊間符號的引用。

人們把每一個源代碼模塊獨立地編譯,而後按照須要將它們組裝起來,這個組裝模塊的過程就是「連接」。

連接的主要內容就是把各個模塊之間相互引用的部分處理好,使得各個模塊之間可以正確銜接。

連接過程主要包括地址和空間分配、符號決議和重定位。

相關文章
相關標籤/搜索