編譯原理概述

ForTechnology發表於2011-09-20

編譯原理概述

分類: linux 基礎篇 62人閱讀 評論(0) 收藏 舉報
一、編譯過程分析
    編譯軟體讀取源程式(字元流),對之進行詞法和語法的分析,將高階語言指令轉換為功能等效的彙編程式碼,再由彙編程式轉換為機器語言,並按照作業系統對可執行檔案格式的要求連結生成可執行程式.
二、編譯流程表
    C源程式(.c檔案)             (編輯器)--&gt
    預處理過程(.c檔案)                  --&gt
    編譯、優化過程(.s或.asm檔案)  (編譯器)--&gt
    彙編過程(.o或.obj檔案)       (彙編器)--&gt
    連結過程(.exe檔案、.elf檔案、.bin檔案等) (連結器LD)
三、預處理過程
    該階段主要工作是讀取C源程式,對其中的偽指令(以#開頭的指令)和特殊符號進行處理.
 偽指令主要包括以下4個方面:
 (1)巨集定義指令,如#define、#undef等.
    對於#define定義的偽指令,預編譯所要做的就是將程式中的所有巨集定義進行替換.
    對於#undef定義的偽指令,則將取消對某個巨集的定義,使以後該串的出現不再被替換.
 (2)條件編譯,如#ifdef、#ifndef、#else、#endif等.
    對於這些偽指令的引入,使得程式設計師可以通過定義不同的巨集來決定編譯程式對哪些程式碼進行處理.預編譯程式將根據有關的檔案,將那些不必要的程式碼過濾掉.
 (3)標頭檔案包含指令,如#include等.
    採用標頭檔案的目的主要是為了使某些定義可以供多個不同的C原始碼程式使用.因為在需要用到這些定義的C源程式中,只需加上一條#include語句即可,而 不必再在此檔案中將這些定義重複一遍.包含到C源程式中的標頭檔案可以是系統提供的,這些標頭檔案一般被放在/usr/include目錄下.在程式 中#include它們要使用尖括號(<>);另外開發人員也可以定義自己的標頭檔案,這些檔案一般放在同一目錄下,此時在#include中 要用雙引號("").
 (4)特殊符號,預編譯程式可以識別一些特殊符號.
 
    通過上面的描述可知,預編譯程式所完成的基本上是對源程式的"替代"工作.經過這種替代,生成一個沒有巨集定義、沒有條件編譯指令、沒有特殊符號的輸出檔案.這個檔案的含義同沒有經過預處理的源程式是相同的,但內容有很大的不同.
   在Linux系統中,編譯的各個過程是可控的.使用者可通過命令gcc -E hello.c -o hello.i,比較預處理前後檔案大小的變化.可以發現,預處理後生成的hello.i檔案遠大於hello.c,這是因為預處理後將庫函式都拷貝到原始檔中了.

   預編譯結束後,檔案仍然是.c檔案.在Linux中,預編譯後生成了.i檔案.
 
四、編譯、優化過程
    編譯的主要工作是通過詞法分析和語法分析,在確認所有的指令都符合語法規則後,將其翻譯成等待的中間程式碼表示或彙編程式碼.
 
    優化是編譯系統中一項比較艱深的計數.它涉及的問題不僅同編譯技術本身有關,而且同機器的硬體環境也有很大的關係.優化主要有兩個方面:
 一是考慮如何充分利用機器的各個硬體暫存器存放的有關變數的值,以減少對記憶體的訪問次數.
 二是調整使目的碼比較短,且執行效率比較高.
    經過優化得到的彙編程式碼必須經過彙編程式的彙編轉換成相應的機器指令,才可能被機器執行.
 
    編譯、優化的C源程式變成了.S或.asm的彙編程式.
 
五、彙編過程
   彙編過程是指將組合語言程式碼翻譯成目標機器指令.目標檔案就是與源程式等效的目標機器語言程式碼.目標檔案由段組成.通常一個目標檔案中至少有兩個段:
   程式碼段:該段中所包含的主要是程式的指令.該段一般是可讀和可執行的,但不可寫.
   資料段:主要存放程式中要用到的各種全域性變數或靜態的資料.一般資料段都可讀、可寫、可執行.
 
   實際上,彙編就是查表,根據指令通過查表生成相應的機器程式碼.
 
   彙編後的C源程式變成了.o或.obj的目標程式.
 
六、連結過程
   由彙編過程生成的目標檔案並不能立即被執行.其中還存在著一些問題.
   比如,某個原始檔中的函式可能引用了另一個原始檔中定義的某個符號(如變數或函式呼叫);程式中可能呼叫了某個庫檔案中的函式.所有這些問題都需要經過連結過程方能得到處理.
   連結過程的主要工作是將有關的目標檔案彼此相連線,也即將在一個檔案中引用的符號同該符號所在另外
   一個檔案中的定義連線起來,使得所有這些目標檔案能成為一個能被作業系統裝入執行的統一整體.
 舉例:
   一工程中包含三個檔案,分別是file1.c、file2.c、file3.c,通過編譯、彙編分別生成了file1.o、file2.o、 file3.o,最後連結器將三個.o檔案根據平臺的要求連結成一個可執行檔案.在x86系統中,生成了.exe檔案.在Linux系統中,生成 了.elf檔案;在嵌入式系統中,生成了.bin檔案.
 
 根據開發人員指定的同庫函式的連結方式不同,連結處理可分為兩種:
 (1)靜態連結
    在這種連結方式下,函式的程式碼將從其所在地靜態連結庫中被拷貝到最終的可執行程式中.這種,該程式在被執行時,這些程式碼將被裝入到該程式的虛擬地址空間中.靜態連結庫實際上是一個目標檔案的集合,其中的每個檔案含有庫中的一個或一組相關函式的程式碼.
   
 (2)動態連結
    在這種連結方式下,函式的程式碼被放到稱作是動態連結庫或共享物件的某個目標檔案中.連結程式此時所
作的只是在最終的可執行程式中記錄下共享物件的名字以及其它少量的登記資訊.在此可執行檔案被執行時,動態連結庫的全部內容將被對映到執行時相應程式的虛擬地址空間.動態連結程式將根據可執行程式中記錄的資訊找到相應的函式程式碼.
   
Attention!!!
 (1)一般情況下,只需要知道分成編譯和連結兩個階段即可.
    編譯階段是將源程式(.c)轉換為目的碼(.obj).
    連結階段是將源程式轉換成的目的碼(.obj)與你程式中呼叫的庫函式對應的程式碼連結起來形成的可執行檔案(.exe等).
 (2)通過上面的描述可知:
    無論是什麼平臺,如x86平臺、Linux平臺還是ARM平臺,預編譯後的.c檔案都相同,這與平臺無關.但編譯後生成的.s或.i檔案,根據平臺的不同,生成的結果也不同.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-707942/,如需轉載,請註明出處,否則將追究法律責任。

相關文章