gcc的編譯過程

我們在linux下寫的.c文件,編譯的時候只需要gcc xxxx.c 默認生成可執行文件a.out,其實編譯的過程要經歷四個過程,在此之前我先不講這四個過程,我先講一下,Linux下gcc的使用方法和選項

gcc的使用方法和選項

gcc的使用方法

gcc [選項] 文件名
在linux下我們可以使用 gcc --help 查看gcc的各個選項

gcc的常見選項

選項 含義
-E 只進行預處理 ,不編譯
-S 只編譯,不彙編
-c 只編譯、彙編,不鏈接
-g 包含調試信息
-o 輸出指定文件名
-L 指定鏈接所需庫(動態庫或靜態庫)所在路徑
-l(小寫L) 指定所需鏈接庫的庫名
-Werror 不區分警告和錯誤,遇到任何警告都停止編譯
-Wall 開啓大部分警告提示
-I(大寫i) 指定include包含文件的搜索目錄
-ansi ANSI標準
-std=c99 C99標準
-O0 關閉所有優化選項
-O1 第一級別優化,使用此選項可使可執行文件更小、運行更快,並不會增加太多編譯時間,可以簡寫爲-O
-O2 第二級別優化,採用了幾乎所有的優化技術,使用此選項會延長編譯時間
-O3 第三級別優化,在-O2的基礎上增加了產生inline函數、使用寄存器等優化技術
-Os 此選項類似於-O2,作用是優化所佔用的空間,但不會進行性能優化,常用於生成最終版本

gcc的編譯過程

gcc編譯的過程可分解爲4個大的步驟:

  1. 預處理(Preprocessing)
  2. 編譯(Compilation)
  3. 彙編(Assembly)
  4. 鏈接(Linking)

下面爲了更加容易理解,我用繪圖的方式展現編譯過程
在這裏插入圖片描述
下面我們講具體的講一下gcc編譯步驟
1、預處理
預處理是讀取c源程序,對其中的僞指令和特殊符號進行“替代”處理;經過此處理,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經過預處理的源文件是相同的,仍然是C文件,但內容有所不同。僞指令主要包括以下三個方面:
(1)宏定義指令,如#define NAME TokenString, #undef以及編譯器內建的一些宏,如__DATE__,FILE, LINE, TIME,
__FUNCTION__等。
(2)條件編譯指令,如#ifdef, #ifndef, #else, #elif, #endif等。
(3)頭文件包含指令,如#include “FileName”或者#include 等。

預處理的過程主要包括以下過程:

  1. 將所有的#define刪除,並且展開所有的宏定義
  2. 處理所有的條件預編譯指令,比如#if 、#ifdef、#elif、#else、#endif等
  3. 處理#include預編譯指令,將被包含的文件插入到該預編譯指令的位置。
  4. 刪除所有註釋“//”和“ /* */”
  5. 添加行號和文件標識,以便編譯時產生調試用的行號及編譯錯誤警告行號。
  6. 保留所有的#pragma編譯器指令,因爲編譯器需要使用它們

通常使用以下命令來進行預處理,參數-E表示只進行預處理:
gcc -E hello.c -o hello.i
也可以使用以下指令完成預處理過程,其中從cpp是預處理器:
cpp hello.c > hello.i

預處理後的結果hello.i還是c語言源代碼,我們可以使用cat或vim命令查看它的代碼
vim hello.i
2、編譯
編譯程序所要做的工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規則之後,將其翻譯成等價的中間代碼表示或彙編代碼。關於編譯環節想要了解更多的可以參考一下其他博客介紹。

我們可以使用下面命令進行編譯生成彙編文件
gcc -S hello.i > hello.s
我們可以使用cat命令查看它的代碼
cat hello.s
3、彙編
彙編過程實際上把彙編語言代碼翻譯成目標機器指令的過程。對於被翻譯系統處理的每一個c語言源程序,都將最終經過一處理而得到相應的目標文件。

我們可以用下面命令進行彙編:
gcc -c hello.s -o hello.o

4、鏈接
彙編程序生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。例如,在某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變量或者函數調用等);在程序中可能調用了某個庫文件的函數,等等,所有的這些問題,都需要經過鏈接才能得以解決,鏈接程序的主要工作就是將有關的目標文件彼此相連接,也即將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成爲一個能夠被操作系統裝入執行的統一整體,也就是可執行程序,根據開發人員指定的庫函數的鏈接方式的不同,鏈接處理可分爲兩種:①靜態鏈接 ②動態鏈接。
對於可執行文件中的函數調用,可分別採用動態鏈接或靜態鏈接的方法。使用動態鏈接能夠使最終的可執行文件比較短小,並且當共享對象被多個進程使用時能節約一些內存,因爲在內存中只需要保存一份此共享對象的代碼。但並不是使用動態鏈接就一定比使用靜態鏈接要優越,在某些情況下動態鏈接可能帶來一些性能上的損害。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章