在linux中進行c/c++程式設計,用到的編譯器一般是gcc。
c程式的編譯過程通常分為了4步,分別為預處理→編譯→彙編→鏈結
。與其他的環境比較,在gcc中我們可以更加方便直觀的看到編譯過程中每一步的結果。
預處理
預處理通常是對程式中的以#
開頭的語句進行處理,例如檔案包含,巨集定義等。
預處理主要做了下面幾個工作
1. 檔案包含語句"#include"
預處理時,檔案包含語句將被刪除,並將包含檔案的內容全部拷貝到該位置。如果被包含的檔案中還有檔案包含語句,將繼續對語句進行拷貝替換,一直遞迴進行下去,直到檔案中不再有包含語句。
2. 巨集定義"#define"
預處理時,巨集定義語句將被刪除,並將所有巨集定義展開,所有用到巨集的地方全部用目標字元替換。
3. 條件預編譯指令"#if","#ifdef","#ifndef","#elif","#else","#endif"
條件預編譯指令將按照實際情況,將符合條件部分的語句塊保留,不符合條件的語句塊刪除。並且將條件預編譯指令刪除。
4. 注釋"//""/**/"
所有注釋符號及內容全部刪除。
5. 新增行號和檔名標示
如"#line 1 "d:\\program files (x86)\\microsoft visual studio 12.0\\vc\\include\\stdio.h""
。如果開啟預處理後的.i檔案將發現很多類似於這樣的資訊,而自己寫的**通常在最後。這些行號和檔名在編譯時會被編譯器忽略。這些資訊的作用是讓編譯器報錯時能夠準確找到錯誤和相關資訊的位置。
6."#pragma"
在所有的#
開頭的語句中"#pragma"
要特殊對待,因為這條語句並不是在預處理時進行處理,而是在編譯時處理的語句。因此,預處理時,該語句將會原樣保留。
在vs環境下,可以在專案的屬性中對預處理進行設定,可以實現自動新增巨集定義,不刪除注釋,不新增行號等特殊要求。
從預處理的工作中不難發現,並不是所有以#
開頭的語句都是在預處理時處理,編譯器也需要一些提示資訊,這些資訊也是#
開頭的,如上面提到的"#line ","#pragma"
。
在gcc中如果需要進行預處理,並得到預處理的結果檔案,可以使用命令
gcc -e test.c -o test.i
檔案test.i
便是預處理後的結果。
編譯
編譯是將預處理過後的**進行詞法分析的過程。直觀來看編譯就是將c語言**翻譯成為彙編**的過程。
c語言的編譯過程並不是由c**直接到機器語言的過程,而是需要彙編**作為中介,從c**到彙編**然後再到機器語言的過程。
通過對編譯結果的彙編**進行分析,我們可以知道程式到底都做了哪些事情,每乙個步驟到底是如何實現的。
在gcc中,如果我們想要得到編譯的結果,可以使用命令
gcc -s test.i -o test.s
其中test.i
為預處理的結果,
test.s
為編譯的結果。
另外也可以用下面的命令,直接由c源**一次性經過預處理和編譯得到編譯結果
gcc -s test.c -o test.s
其中test.c
為程式源**。
彙編
彙編是將編譯得到的彙編**翻譯成機器語言的過程,即得到目標檔案。
要得到目標檔案,可以使用命令
gcc -c test.s -o test.o
同樣也可以直接由c源**得到彙編結果
gcc -c test.c -o test.o
其中「為目標檔案。
鏈結
同樣一步到位的命令如下
gcc test.c -o test
其中test為最終的可執行檔案。
只需在相應的目錄下執行命令
./test
便可執行程式。
對於多檔案的專案,每個檔案都對應了乙個目標檔案。要得到最後的可執行檔案,不僅需要附加的目標檔案,還需要將每個原始檔對應的目標檔案進行鏈結。
鏈結的命令如下
gcc -c test1.s test2.s ... -o test
或者一步到位
gcc -c test1.c test2.c ... -o test
同樣test
為最後的可執行檔案
對於linux系統來說,檔案並不需要副檔名。但是對於gcc來說檔案的副檔名是必須的,gcc要通過它來確定檔案型別,並進行對應的處理方案。
gcc中常用到的副檔名有
副檔名型別
處理方式
.cc原始檔
預處理、編譯、彙編
.cc++原始檔
預處理、編譯、彙編
.ccc++原始檔
預處理、編譯、彙編
.cxx
c++原始檔
預處理、編譯、彙編
.mobjective-c原始檔
預處理、編譯、彙編
.ic原始檔的預處理結果
編譯、彙編
.iic++原始檔的預處理結果
編譯、彙編
.s組合語言原始檔(編譯結果)
彙編.s
組合語言原始檔(人工編寫)
預處理、彙編
.h標頭檔案
通常不在命令中出現
在gcc的編譯過程中,每一步的編譯結果都要使用對應的副檔名。對於最後的可執行檔案,由於操作可執行檔案的並不再是gcc,而是linux系統進行執行。因此,編譯最後得到的可執行檔案通常不需要副檔名。
.s
和.s
在**中看到.s
和.s
都是組合語言原始檔,那兩者有什麼區別呢。
首先,在對c語言進行編譯的過程中,我們用到的副檔名應該是.s
。那麼可以知道,編譯的結果應該是.s
檔案。那什麼時候才會出現.s
檔案呢。
**中看到,對於兩種檔案的處理方式有所不同。
.s
檔案只需要進行彙編處理
.s
檔案則需要先進行預處理,然後再進行彙編處理
為什麼要進行預處理呢?
對組合語言有所了解的應該知道,使用組合語言程式設計是可以進行巨集定義的,類似於c語言中的#define
。要對這樣的彙編檔案進行彙編,則必須先要對的巨集進行處理。因此在彙編之前,需要先進行預處理操作。而在編譯得到的彙編檔案中則不存在這樣的語句,因此不需要預處理命令。
簡單的說
.s
檔案通常是高階語言編譯得到的檔案;
.s
檔案通常是人工進行組合語言程式設計,即實模式程式設計時得到的檔案。檔案在彙編之前需要對檔案中的一些語句進行預處理。
通過上面的討論我們還可以了解到,gcc不但可以對c語言和c++語言進行編譯,還可以對組合語言進行編譯。
通過GCC編譯器編譯c語言
可以利用gcc的引數來控制執行的過程,這樣就可以更深入的了解編譯c程式的過程。下面將通過對乙個程式的編譯來演示整個過程。include int main 1 預處理 編譯器將c程式的標頭檔案編譯進來,還有巨集的替換,可以用gcc的引數 e來參看。作用 將hello.c預處理輸出hello.i 2 編...
C語言基礎 GCC編譯器
預處理 pre processing 編譯 compiling 彙編 assembling 鏈結 linking 編譯器通過程式的副檔名來分辯編寫源程式所用的語言。由於不同的程式所需要執行編譯的步驟是不同的,因此gcc根據不同的字尾名對它們進行相應處理。後 綴 名 所對應的語言 編 譯 流 程 cc...
GCC編譯器學習
不同的平台如x86和arm,一段程式跑起來到最下面會轉成彙編,彙編要轉成機器碼,機器碼會由於硬體平台不同而不同。有時候要程式設計序在arm上跑,必須針對arm寫程式,但是在arm上寫程式很麻煩 沒有很好的開發工具 所以我們就在x86平台上 windows 上把程式寫好,但是編譯的時候告訴它我們要執行...