程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 編譯到底做了什麼(***.c,編譯到底做.c

編譯到底做了什麼(***.c,編譯到底做.c

編輯:關於C語言

編譯到底做了什麼(***.c,編譯到底做.c


 (第一次寫博客,好激動的說.......)   我們知道,一個程序由源代碼到可執行文件往往由這幾步構成: 預處理(Prepressing)-> 編譯(Compilation)-> 匯編(Assembly)-> 鏈接(Linking)。   編譯過程就是把預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化後生產相應的匯編代碼文件,這個過程往往是我們所說的整個程序構建的核心部分。那麼,這個核心部分究竟做了什麼呢。   各位看官容我挽起袖子,且聽我娓娓道來。   編譯器做了什麼?   從最直觀的角度來說,編譯器就是將高級語言翻譯成機器語言的一個工具。   以 C語言為例,解釋一下 ***.c -> ***.o 的過程。 假設test.c有下面一段代碼   array[index] = (index + 4) * (2 + 6);   下面就來談談這個表達式是如何翻譯成機器語言的過程。   這個過程主要有如下五步,看起來好長的樣子,看官需靜下心來慢慢看。。。。   1.詞法分析 -- 將源代碼字符序列分割成一系列的記號   源代碼程序被輸入到掃描器(Scanner)。   掃描器的任務就是:運用一種有限狀態機(Finite State Machine)的算法,將源代碼字符序列分割成一系列的記號(Token)。還有一些其他工作(將標識符放到符號表,將數字、字符串放到文字表中)   如下圖(因為表格換頁了,所以拍出來是這個樣子,望海涵)     詞法分析產生的記號可以分為如下幾類:關鍵字、標識符、字面量(包括數字、字符串等)和特殊符號(+ - * /)。   需要注意的是:C語言的宏替換和文件包含等工作一般不是編譯器做的,而是交給一個獨立的預處理器。   有一個叫做lex的程序可以實現詞法掃描。   2.語法分析 --  產生語法樹(以表達式為節點的樹)   語法分析器(Grammar Parser)將對上面產生的記號進行語法分析,產生語法樹(Syntax Tree)-- 采用的是上下文無關語法的分析手段。  簡單的說,語法分析器生成的語法樹就是以表達式(Expression)為節點的樹。  如圖  語法分析階段必須對好多東西(符號的含義和優先級)進行區分,若出現了不合法(如括號不匹配,表達式缺少操作符等),編譯器就會報告語法分析階段的錯誤。  僅僅是完成了對表達式語法層面的分析,並不了解這個語句是否真正有意義。  語法分析也有一個現成的工具叫yacc(Yet Another Compiler Compiler)。   3.語義分析  --  將語法樹中節點標明含義   接下來就是,由語義分析器(Semantic Analyzer)來完成。   任務就是:為語法樹的表達式標識類型。就是下面這個樣子,多了類型   如圖      符號和數字是最小的表達式。   編譯器所能分析的語義是靜態語義。(動態語義不能被分析)   靜態語義:在編譯階段可以確定的語義,通常包括聲明和類型的匹配,類型的轉換。   動態語義:在運行期才能確定的語義,比如將0作為除數是一個運行期語義錯誤。   4.中間語言生成  -- 一個優化過程   現代的編譯器有著很多層次的優化,這裡介紹的是一個源碼級優化器(Source Code Optimizer),會在源碼級別進行優化。比如例子中的(2 + 6),因為在編譯階段可以確定為8,所以這個表達式被優化掉了。    因為直接在語法樹上做優化是比較困難的,所以源代碼優化器往往將整個語法樹轉換成中間代碼(Intermediate Code),就是語法樹的順序表示(已經非常接近目標代碼了)。  中間代碼有很多類型,在不同的編譯器有著不同的表現形式,常見的有:三地址碼(Three-address Code)、P代碼(p-Code)。   中間代碼使得編譯器可以分成前端和後端。 前端:負責產生機器無關的中間代碼 後端:將中間代碼轉換成目標代碼   5.目標代碼生成與優化(這裡開始就是後端了,前面都是前端)  編譯器後端主要包括:代碼生成器(Code Generator)和目標代碼優化器(Target Code Optimizer)。  代碼生成器:將中間代碼轉換成目標機器代碼。這個過程非常依賴於機器,因為不同的機器有不同的字長,寄存器,整數數據類型和浮點數數據類型等。 對於我們的例子,可能會生成下面的代碼序列(用x86的匯編來表示),如圖   目標代碼優化器:對上述的目標代碼進行優化。比如:選擇合適的尋址方式,使用位移來代替乘法運算,刪除多余的指令等。 對於我們的例子,有可能會優化成這個樣子。 如圖。   ------  我是分割線   ------   好了,忙活了這麼久,源代碼終於變成了目標代碼。 這時候問題來了,index和array的地址還沒有確定。若用把目標代碼用匯編器編譯成真正能在機器上執行的指令,這兩個地址從何而來呢。 若index和array定義在跟上面的源代碼同一個編譯單元裡,那麼編譯器可以為它們分配空間,確定它們的地址。 若定義在其他模塊呢?說來就話長了。。。。。。   附在那本書的一些話:(助於理解) (1).現代的編譯器可以將一個源代碼文件編譯成一個未鏈接的目標文件,然後由鏈接器最終將這些目標文件鏈接起來形成可執行文件。 (2).匯編器是將匯編代碼轉變成機器可以執行的指令,每一個匯編語句幾乎都對應一條機器指令。 (3).所以匯編器的匯編過程相對於編譯器來講比較簡單,它沒有復雜的語法,也沒有語義,也不需要做指令優化,只是根據匯編指令和機器指令的對照表一一翻譯就可以了。 (4).經過預編譯、編譯和匯編直接輸出目標文件(Object File)。     參考文獻《程序員的自我修養--鏈接、裝載與庫》 P41-P48 (其實就是摘抄整理了一下,哈哈)
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved