前幾天有個朋友問我關於C語言的編譯原理和編譯的過程,當時我也沒有說明白,今天特意在書上和網上查閱資料,簡單的總結了一下關於C語言的編譯原理及過程。
整合式開發環境是用於提供者開發環境的應用程式,一般包括代碼編輯器、編譯器,調試器和圖形化使用者介面工具。整合了代碼編寫功能、分析功能、編譯功能、調試功能等一體化的開發軟體服務套。
編輯器:編寫代碼的一些視窗。
編譯器:檢查使用者代碼的一些語法錯誤,將其編譯成彙編代碼。
彙編器:將編譯出來的彙編檔案編譯成一定功能的目標代碼。
連接器:將目標代碼串連成可執行檔。
一個編譯器包括一下幾個部分:
1.詞法分析:掃描器(Scanner)將源代的字元序列分割成一系列的記號(Token)。lex工具可實現詞法掃描。
2.文法分析:文法分析器將記號(Token)產生文法樹(Syntax Tree)。yacc工具可實現文法分析(yacc: Yet Another Compiler Compiler)。
3.語義分析:靜態語義(在編譯器可以確定的語義)、動態語義(只能在運行期才能確定的語義)。
4.原始碼最佳化:原始碼最佳化器(Source Code Optimizer),將整個文法書轉化為中間代碼(Intermediate Code)(中間代碼是與目標機器和運行環境無關的)。中間代碼使得編譯器被分為前端和後端。編譯器前端負責產生機器無關的中間代碼;編譯器後端將中間代碼轉化為目標機器代碼。
5.目標代碼產生:代碼產生器(Code Generator).
6.目標代碼最佳化:目標代碼最佳化器(Target Code Optimizer)。
瞭解了以上幾個概念後,正式進入C語言的編譯原理及過程:
C語言編譯的完整過程:C來源程式--->先行編譯處理--->編譯、最佳化處理--->組譯工具--->連結程式--->可執行檔
以上過程可以簡單的分為4大步驟:預先處理--->編譯--->彙編--->連結
1.預先處理:包括以下幾個過程
a.宏定義指令:將所有的#define刪除,並且展開所有的宏定義
b.條件編譯指令:處理所有的條件先行編譯指令,比如#if #ifdef #elif #else #endif等
c.標頭檔包含指令:處理#include 先行編譯指令,將被包含的檔案插入到該先行編譯指令的位置
d.特殊符號指令:先行編譯器可研識別一些特殊的符號,例如:刪除所有注釋 “//”和”/* */”
e.添加行號和檔案標識,以便編譯時間產生調試用的行號及編譯錯誤警告行號
f.保留所有的#pragma編譯器指令,因為編譯器需要使用它們
使用以下命令來進行預先處理:
gcc -E *.c -o *.i
參數-E表示只進行預先處理
2.編譯 :編譯過程就是把預先處理完的檔案進行一系列的詞法分析,文法分析,語義分析及最佳化後產生相應的彙編代碼
使用一下指令進行編譯:
gcc –S *.i –o *.s
3.彙編: 彙編器是將彙編代碼轉變成機器可以執行的命令,每一個彙編語句幾乎都對應一條機器指令。根據彙編指令和機器指令的對照表一一翻譯即可。用一下指令進行彙編:
gcc –c *.c –o *.o
4.連結:通過調用連結器ld來連結程式運行需要的一大堆目標檔案,以及所依賴的其它庫檔案,最後產生可執行檔., 連結的主要內容是把各個模組之間相互引用的部分處理好,使得各個模組之間能夠正確地銜接,連結分為靜態連結和動態連結。
靜態連結是指在編譯階段直接把靜態庫加入到可執行檔中去,這樣可執行檔會比較大。靜態庫檔案:是一個二進位檔案,存放的功能函數實現,在檔案編譯時間要訪問檔案,編譯之後靜態庫檔案可以刪除
而動態連結則是指連結階段僅僅只加入一些描述資訊,而程式執行時再從系統中把相應動態庫載入到記憶體中去,動態庫檔案:是一個二進位檔案,存放的功能函數實現,在檔案執行時要訪問檔案,編譯時間不需要動態庫檔案
linux動態庫檔案尾碼名:.so
window動態庫檔案尾碼名:.dll
目標檔案:是一個二進位檔案,由函數組成,不能單獨執行,給主函數的檔案調用。