lcc原始碼詳解之概述

來源:互聯網
上載者:User

編譯器怎樣把C來源程式翻譯成機器代碼呢?相信你一定很好奇並想看看具體的例子。好,下面就以一個非常簡單例子來說一下編譯器的整個工作過程。

來源程式:

int round (f) float f; {

              return f+0.5;

}

第一階段:預先處理。

預先處理是指宏擴充、引入標頭檔、選擇條件編譯代碼等工作。其實就是你經常使用的#define、#include<xxx.h>、#ifdef xxx 等語句。預先處理程式是作為獨立的進程執行的,而且可以混用,比如lcc編譯器可以使用GCC的前置處理器(這是因為大家都遵循同一個標準ANSI C,所以說標準這東西就是好,一有標準大家就不會亂套)。但對本例而言,經過預先處理後原始碼還是這個樣子,所以這裡就不打算深入介紹前置處理器了,而且它也不在討論之內,不會影響對原始碼的閱讀。

第二階段:詞法分析。

詞法分析的任務就是分解出一個個的單詞(token)。就像我們英漢翻譯一樣,你不妨把C來源程式當做英語,把機器碼當做漢語。好,現在給你一段英文(C來源程式),要你把它翻譯成中文(機器碼)。你想一下你會怎麼做?當然你首先要把這一段英文分解成一個個的單詞,逐個對照牛津詞典(C語言詞法規則)。同樣,詞法分析無非就是把來源程式肢解成一個個詞法單位,即單詞,然後用一張表記下來,等文法分析的時候再拿出來看看。現在這個例子就會被lcc肢解成下面的這張單詞表:

單詞編碼                         附加值

INT                                   inttype

ID                                     "round"

'('

ID                                       "f"

')'                                       

FLOAT                              floattype

ID                                       "f"

';'             

'{'

RETURN         

ID                                         "f"

'+'

FCON                                  0.5

';'

'}'

EOI

其中,EOI代表結束符。附加值給出了單詞的更多資訊。雖然你現在可能還在對上面表中的某些符號如FCON是什麼東西耿耿於懷,其實大可不必,你現在只須對詞法分析有一個大致的瞭解即可,即知道詞法分析到底是幹什麼的就可以了,至於它具體怎麼運作,那是日後的分析了。

第三階段:文法及語義分析。

文法分析就是分析是不是符合C語言的文法要求和規定,關於C語言本身的文法可以參見K&R的經典《The C Programming Language》的附錄A,那裡對ANSI C進行全面的解釋。語義分析就是分析是不是符合語義,比如某些句子可能沒有語法錯誤但有語義錯誤,比如代碼 a = b+3; 使用了未定義的變數a,這就是語義錯誤。文法分析最終會產生一片森林(資料結構應該學過了吧),森林裡有很多樹,每棵樹都叫做抽象文法樹(Abstract Syntax Tree,簡稱AST)。就本例而言,lcc會產生如下兩棵AST:

 

 

 

其中,每個節點的形式是“操作符+類型”。詳細如下:

 

ASGN+F: Assignment + Float 即浮點數賦值運算。

ADDRF+P: Address-Function + Pointer 即指向函數參數的指標,也就是參數的地址。

CVD+F: Convert Double to Float 即將Double轉換為Float,同理CVD+I 就是Double轉換為Int 以此類推。

INDIR+D: Indirection Double 即取值,值的類型為double。其中INDIR代表取值操作,+號後面代表值的類型。如INDIR+F就是取浮點值。

       第一個AST應該從右下角的" caller "f" ---> double " 逆著箭頭並逆時針看。總的流程是這樣:從調用者(caller)的f處取值,此值是 double類型,將其轉換成float類型並賦值給函數round(被調用者,callee)的參數f。

       第二個AST應該從左下角的" callee "f" ---> float " 逆著箭頭並順時針看。總的流程是這樣:從參數f處取值,並轉換成double,然後與double類型的常量0.5進行雙精確度的加運算。運算完後將結果轉換成 int型並返回。

       看到這裡是不是把原始碼給忘了?回過頭看看開頭處的原始碼,傳回值是 int 類型沒錯,參數類型是 float 類型,常量0.5預設是 double 類型。所以一系列的類型強轉就在程式員不知不覺的情況下發生了:先是把double類型的實參(假設的)轉換成 float 類型的傳給參數,由於0.5是double類型的,不得已又再次轉換成double類型的,然後才進行運算,最後看看傳回型別,不好,是int類型,無奈之下又強轉為int類型,最終把結果返回給調用者。

        所以說現在使用進階語言編程的程式員真是幸福啊!隨隨便便寫下像這樣的代碼:

       int a = 5;   double b = 2.34; char c = a + b;

還沒事。因為C編譯器已經默默無聞地忍受了這一切!這也是使用進階語言的好處之一啊。但是機器始終是機器,你要告訴它是什麼類型的運算它才能算。學過彙編的都知道,就加法而言,就有整型加,浮點型加等等。編譯器的很重要的任務之一就是隱藏這些細節,使編程簡單。

第四階段:中間代碼的產生。

中間代碼階段將上面的AST轉換成DAG(Directed Acyclic graph,無迴圈有向圖)。如:

 

 

 

 

這張圖與之前的區別在於將形如 ASGN+F 的改成 ASGNF 以示區別。其中CNST+D變成引用標號為2的靜態變數。標號1表示round的結尾。

第五階段:彙編代碼的產生。

你可以看到,DAG已經可以很形象地描述執行代碼了。lcc的代碼產生器通過對DAG加註釋的方法產生彙編代碼。注釋結果如:

 

 

 

每個函數的入口和出口都是一樣的彙編代碼,所以後端就會準備好一張代碼模板,我們只要把產生的程式碼插進合適的位置就可以了。就X86和DOS/WinNT而言,lcc與MASM或TASM彙編器協同合作,將產生的彙編代碼串連成在特定的體繫結構和作業系統上的機器碼。

 

關於彙編的知識自己掌握,這裡不討論了,以免陷入不必要的細節。

小結:

          從C來源程式到彙編代碼,我們很快地走了一遍,瞭解了lcc的工作過程。下一節就進入正題,進行代碼注釋。注釋的順序與作者著作《A retargetable.....》一樣,因為照常理應該是從詞法分析開始,但詞法分析出來的單詞如何管理呢?這就涉及到符號表,符號表怎樣動態增長怎樣儲存呢?這就涉及到記憶體管理。同樣符號表與類型表有千絲萬縷的聯絡。符號表的符號名字又涉及到字串的管理。所以自底向上的順序是:

記憶體管理---->字串管理----->符號表管理、類型表管理----->詞法分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.