GCC編譯C:C++的四個過程


前言

從源碼到可執行程序,經歷四個過程:預處理、編譯、匯編和鏈接,前三步由使用編譯器來完成、鏈接由鏈接來完成。

  • 編譯器將編譯工作主要分為預處理,編譯和匯編三部
  • 連接器的工作是把各個獨立的模塊鏈接為可執行程序
  • 靜態鏈接在編譯期完成,動態鏈接在運行期完成

階段

階段名 生成文件類型
預處理(-E 生成 .i文件
編譯(-S 生成 .s 文件
匯編(-c 生層 .o 文件
連接(-l 生成 可執行 文件

gcc 選項

選項 含義
-v 查看編譯器的版本,像是 gcc 執行時的詳細過程
-o <file> 輸出名為 file 的文件,這個不能與源文件名稱相同
-E 只預處理,不會編譯、匯編、鏈接
-S 只編譯,不會匯編、鏈接
-c 只編譯和匯編,不會連接

在編譯過程中,除非使用了-E、-S、-C選項(或者編譯出錯阻止了完整的編譯過程),否則最后的步驟都是鏈接

文件類型

編譯器支持許多和C語言程序相關的擴展名,對它們的說明如下:

擴展名 說明 后期操作 命令
.c C程序源代碼,在編譯之前要先進行預處理。 預處理、編譯、匯編 gcc -E test.c -o test.i
.C **C++**程序源代碼,在編譯之前要先進行預處理。 預處理、編譯、匯編
.cc **C++**程序源代碼,在編譯之前要先進行預處理。 預處理、編譯、匯編
.cxx **C++**程序源代碼,在編譯之前要先進行預處理。 預處理、編譯、匯編
.m OC程序源代碼,在編譯之前要先進行預處理。 預處理、編譯、匯編
.i C程序預處理輸出,可以被編譯。可以理解為預處理翻譯(interpret)后的文件 編譯、匯編 gcc -s test.i -o test.s
.ii **C++**程序預處理輸出,可以被編譯。可以理解為預處理翻譯(interpret)后的文件 編譯、匯編
.h C程序頭文件。(為了節省時間,許多源文件會包含相同的頭文件,GCC 允許事先編譯好頭文件,稱為“預編譯頭文件”,它合適情況下自動被用於編譯。) 通常不會出現在命令行
.s 匯編語言。 匯編 gcc -c test.s -o test.o

1 GCC編譯C/C++的四個過程

gcc 是 GUN Compiler Collection的縮寫。

預處理(pre-processing),E:插入頭文件,替換宏,展開宏

gcc -E main.c -o main.i

編譯(Compiling)S:編譯成匯編

gcc -S main.i –o main.s

匯編(Assembling) c:編譯成目標文件

gcc –c main.s –o main.o

鏈接 (Linking):鏈接到庫中,變成可執行文件

gcc main.o –o main
./main 

也可以一次性完成:

gcc main.c –o main

但一般情況下生成.o文件比較好,可以重定位文件,讓別人使用

1.1 預處理

  • 處理所有的注釋,以空格代替
  • 將所有的#define刪除, 並且展開所有的宏定義
  • 處理條件編譯指令#if, #ifdef, #elif, #else, #endif 處理條件編譯指令
  • 處理處理#include, 展開(evolve)被包含的文件
  • 保留編譯器需要使用的#pragma指令

C語言代碼在交給編譯器之前,會先由預處理器進行一些文本替換方面的操作,例如宏展開、文件包含、刪除部分代碼等。

在正常的情況下,GCC 不會保留預處理階段的輸出文件,也即 .i 文件。然而,可以利用 -E 選項保留預處理器的輸出文件,以用於診斷代碼。-E選項指示 GCC 在預處理完畢之后即可停止。

a、默認情況下,預處理器的輸出會被導入到標准輸出流(也就是控制台)

gcc -E main.c 

b、可以利用-o選項把它導入到某個輸出文件

gcc -E main.c -o main.i

或者

 gcc -E main.c > main.i 

表示把預處理的結果導出到 main.i 文件中。

c、使用-C選項阻止預處理器刪除源文件和頭文件中的注釋。

注意,這里是大寫的 -C,不是小寫的 -c。小寫的 -c 表示只編譯不鏈接。

gcc -E -C main.c -o main.i 

對比之前的輸出,可以發現,保留願文件中的注釋

1.2 編譯階段

  • 對預處理文件進行一系列詞法分析,語法分析和語義分析
  • 詞法分析主要分析關健字,標示符,立即數等是否合法
  • 語法分析主要分析表達式是否遵循語法規則
  • 語義分析在語法分析的基礎上進一步分析表達式是否合法
  • 分析結束后進行代碼優化生成相應的匯編代碼文件

編譯器的核心任務是把C程序翻譯成機器的匯編語言(assembly language)。

匯編語言是人類可以閱讀的編程語言,也是相當接近實際機器碼的語言。每種 CPU 架構都有不同的匯編語言。

實際上,GCC 是一個適合多種 CPU 架構的編譯器,不會把C程序語句直接翻譯成目標機器的匯編語言,而是在輸入語言和輸出匯編語言之間,利用一個中間語言,稱為 RegisterTransfer Language(簡稱 RTL,寄存器傳輸語言)。借助於這個抽象層,在任何背景下,編譯器可以選擇最經濟的方式對給定的操作編碼。

通常情況下,GCC 把匯編語言輸出存儲到臨時文件中,並且在匯編器執行完后立刻刪除它們。但是可以使用-S選項,讓編譯程序在生成匯編語言輸出之后立刻停止,而不刪除臨時文件。

-S可以理解為**Save或者Stop**,自己的理解僅供參考記憶。

如果沒有指定輸出文件名,那么采用**-S**選項的 GCC 編譯過程會為每個被編譯的輸入文件生成以.s作為后綴的匯編語言文件。如下例所示:

gcc -S ts.c   

編譯器預處理 ts.c,將其翻譯成匯編語言,並將結果存儲在 ts.s 文件中,可以用文本編輯器察看.

指定輸出文件名

gcc -S main.i -o filename.s

1.3 匯編階段

  • 匯編器將匯編代碼轉變為機器可以執行的指令
  • 每個匯編語句幾乎都對應一條機器指令

目標文件是一種中間文件或者臨時文件,如果不設置該選項,gcc 一般不會保留目標文件,可執行文件生成完成后就自動刪除了。

注意,使用-c選項表示只編譯源文件,而不進行鏈接,因此,對於鏈接中的錯誤是無法發現的。

比如,調用一個不存在的函數,編譯器會提示的錯誤。但是該命令不會輸出錯誤信息。

gcc -c main.s -o main.o

**-c選項:**只編譯不鏈接,僅生成目標文件。

1.4 鏈接階段

gcc main.o -o main.exe

連接器的主要作用是把各個模塊之間相互引用的部分處理好,使得各個模塊之間能夠正確的銜接

靜態鏈接

靜態鏈接使用靜態庫進行鏈接,生成的程序包含程序運行所需要的全部庫,可以直接運行,不過靜態鏈接生成的程序比較大。

動態鏈接

動態鏈接使用動態鏈接庫進行鏈接,生成的程序在執行的時候需要加載所需的動態庫才能運行。

動態鏈接生成的程序體積較小,但是必須依賴所需的動態庫,否則無法執行。

2 clang 分四步編譯main.c

這里用的clang/clang++ 分四步編譯main.c/main.cpp文件

2.1 預處理

clang++ -E main.cpp -o main.ii

2.2 編譯階段,生成匯編

clang++ -S main.ii -o main.s 

2.3 匯編階段,生成目標文件

 clang++ -c main.s -o mian.o

2.4 連接階段

 clang++ mian.o -o main

2.5 執行

 ./main

2.6 源文件

#include <iostream>

int main() {
    std::cout << "Hello Biter !" << std::endl;
    return 0;
}

2.7 四部曲之一步到胃

clang++在編譯的過程中,保存所有編譯過程中產生的文件

2.8 產生中間文件

 clang++ -save-temps main.cpp -o main

2.9 不保存中間文件

 clang++  main.cpp -o main

3 案例

  • 頭文件
//
// Created by langkechanxin on 2021/1/7.
//

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int g_global = 10;
  • 源文件
//
// Created by langkechanxin on 2021/1/7.
//
#include "test.h"

#define LOW  0
#define HIGH 255

int max(int a, int b)
{
    return MAX(a,b);
}

int main()
{

    int c = max(LOW, HIGH); // Call max to get the larger number

    return 0;
}

預處理

gcc -E test.c -o test.i

編譯

gcc -S test.i -o test.s

匯編

gcc -c test.s -o test.o

此時,借助 -c選項,生成的中間產物 test.o ,此文件為二級制文件, 通過 ls -l命令查看一下文件大小:

-rw-r--r-- 1 binny staff 912B Jan 7 15:41 test.o

借助二進制文件查看器,比如 UltraEdit 可以查看到地址范圍:00000000h - 00000390(不包含)這是下一個字節的地址編號。一共 390hB=912B.

連接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM