目錄
引言
?概括介紹
一、預(yù)處理
二、編譯
三、匯編
四、鏈接
總結(jié)
引言
當(dāng)使用集成開發(fā)環(huán)境(IDE)進(jìn)行C語言編程時(shí),點(diǎn)擊"編譯"按鈕后,整個(gè)C程序從源代碼到可執(zhí)行文件的生成過程會(huì)自動(dòng)完成。IDE會(huì)在后臺(tái)為我們執(zhí)行C語言的編譯過程,將源代碼轉(zhuǎn)換為最終的可執(zhí)行文件。雖然IDE隱藏了底層的細(xì)節(jié),但理解編譯過程對(duì)于程序員來說仍然是很有價(jià)值的。
?概括介紹
gcc
和g++
都是GNU編譯器套件(GNU Compiler Collection,簡(jiǎn)稱GCC)的一部分,其中gcc
用于編譯C語言代碼,而g++
用于編譯C++語言代碼。它們的編譯過程在大部分情況下是類似的,但根據(jù)輸入文件的擴(kuò)展名和一些默認(rèn)選項(xiàng)的不同,它們會(huì)調(diào)用不同的編譯器前端,即C前端或C++前端。
下面是gcc
和g++
的編譯過程的概述
-
預(yù)處理(Preprocessing):首先,對(duì)源文件進(jìn)行預(yù)處理。預(yù)處理器將處理源代碼中的預(yù)處理指令,比如以
#
開頭的指令,如#include
、#define
等,并展開宏定義。預(yù)處理后的代碼會(huì)生成一個(gè).i
文件,通常是在臨時(shí)目錄中。 -
編譯(Compiling):接下來,編譯器前端會(huì)將預(yù)處理后的源代碼編譯成匯編代碼(
.s
文件)。此階段會(huì)檢查語法和語義錯(cuò)誤,并進(jìn)行優(yōu)化,但不會(huì)生成可執(zhí)行代碼。 -
匯編(Assembling):匯編器(as)將匯編代碼轉(zhuǎn)換成機(jī)器代碼,并生成目標(biāo)文件(
.o
文件)。 -
鏈接(Linking):最后,鏈接器(
ld
)將目標(biāo)文件與所需的庫文件鏈接在一起,生成最終的可執(zhí)行文件。
下面讓我們?cè)贚inux環(huán)境下簡(jiǎn)單示例C程序編譯過程加深理解
代碼示例(main.c):
#include<stdio.h>
int main(){
printf("Hello Linux\n");
return 0;
}
一、預(yù)處理
預(yù)處理是編譯過程的第一步,它處理以
#
開頭的預(yù)處理指令,并展開宏定義。預(yù)處理器會(huì)執(zhí)行以下主要任務(wù):
處理
#include
指令:將指定的頭文件內(nèi)容插入到源代碼中。這樣,可以在源文件中使用其他函數(shù)或變量的聲明和定義。處理宏定義:將代碼中定義的宏展開為對(duì)應(yīng)的表達(dá)式或語句。例如,
#define MAX_VALUE 100
將會(huì)在源代碼中把所有MAX_VALUE
替換為100
。處理?xiàng)l件編譯指令:如
#ifdef
、#ifndef
、#if
等,這些指令根據(jù)條件判斷是否編譯部分代碼塊。預(yù)處理后的代碼會(huì)生成一個(gè)
.i
文件,這是一個(gè)展開了所有宏和包含了所有頭文件的中間文件。
語法示例
gcc -E main.c -o main.i
?命令中-E是讓編譯器在預(yù)處理之后就退出,不進(jìn)行后續(xù)編譯過程,-o是指定輸出文件名。
使用該指令的結(jié)果是將stdio.h文件全部?jī)?nèi)容插入到main.c形成main.i文件。
可以看到預(yù)處理之后的main.i文件顯然比main.c文件大得多。我們查看一下main.i文件,因?yàn)榇藭r(shí)main.i依然是文本文件。
(使用head指令查看main.i文件)
二、編譯
編譯是預(yù)處理后代碼的第二個(gè)階段。編譯器前端(例如
cc1
或cc1plus
)接收預(yù)處理后的代碼,并將其轉(zhuǎn)換成匯編代碼。在編譯階段,編譯器執(zhí)行以下主要任務(wù):
語法和語義檢查:編譯器檢查代碼是否符合C/C++語法規(guī)則,并進(jìn)行語義分析,以確保代碼沒有邏輯錯(cuò)誤。
生成中間表示:編譯器將代碼轉(zhuǎn)換成中間表示形式,通常是一種低級(jí)的、與特定硬件無關(guān)的表示。
優(yōu)化:編譯器可能對(duì)中間表示進(jìn)行優(yōu)化,以提高程序的執(zhí)行效率和代碼質(zhì)量。
編譯階段不會(huì)生成可執(zhí)行文件,而是將代碼轉(zhuǎn)換成匯編代碼,通常保存為
.s
文件。
語法示例
gcc -S main.i -o main.s
命令中-S讓編譯器在編譯之后停止,不進(jìn)行后續(xù)編譯過程,-o是指定輸出文件名。
編譯成匯編文件大小已經(jīng)非常小了,相對(duì)于預(yù)處理之后的main.i文件小很多。
編譯過程完成后,將生成程序的匯編代碼test.s,這也是文本文件。我們查看一下。
?圖中即為main.s中的匯編代碼。
三、匯編
在匯編階段,匯編器(
as
)接收編譯生成的匯編代碼,并將其轉(zhuǎn)換為機(jī)器代碼。匯編器的任務(wù)包括:
將匯編代碼轉(zhuǎn)換為機(jī)器代碼:將匯編代碼中的匯編指令翻譯成特定硬件架構(gòu)能理解的機(jī)器指令。
生成目標(biāo)文件:生成一個(gè)或多個(gè)目標(biāo)文件(
.o
文件),每個(gè)文件對(duì)應(yīng)一個(gè)源文件或編譯單元。目標(biāo)文件是機(jī)器代碼的二進(jìn)制表示形式,但它們還不是最終可執(zhí)行的程序,因?yàn)槟承┓?hào)引用可能仍然未解析。
語法示例
gcc -c main.s -o main.o
命令中-c
選項(xiàng),它告訴gcc只進(jìn)行編譯,不進(jìn)行鏈接。因此,這個(gè)命令只會(huì)將匯編代碼轉(zhuǎn)換為目標(biāo)文件,而不會(huì)生成可執(zhí)行文件。-o是指定輸出文件名。
目標(biāo)文件test.o
是二進(jìn)制表示的機(jī)器代碼,可以作為鏈接的輸入,用于生成最終的可執(zhí)行文件。
四、鏈接
- 鏈接器接收一個(gè)或多個(gè)目標(biāo)文件,以及所需的庫文件,并將它們合并成最終的可執(zhí)行文件。
- 鏈接器解析目標(biāo)文件中的符號(hào)引用,找到對(duì)應(yīng)的符號(hào)定義,并將符號(hào)重定位,以便正確地指向它們的定義。
- 合并庫文件,生成完整的可執(zhí)行文件,其中包含所有的機(jī)器代碼和解析后的符號(hào)。
語法示例
gcc main.o -o main
命令gcc main.o -o main
是將目標(biāo)文件(main.o
)鏈接為可執(zhí)行文件(main
)的gcc命令。在這個(gè)命令中,我們沒有使用-c
選項(xiàng),因此gcc會(huì)進(jìn)行鏈接操作,生成最終的可執(zhí)行文件。
當(dāng)執(zhí)行gcc main.o -o main
命令時(shí),gcc會(huì)將目標(biāo)文件main.o
與所需的庫文件(如果有的話)一起進(jìn)行鏈接,并生成最終的可執(zhí)行文件main
。這個(gè)可執(zhí)行文件就是可以在Linux下運(yùn)行的C程序。
?執(zhí)行./main
命令會(huì)運(yùn)行名為main
的可執(zhí)行文件。這是我們之前用gcc
命令生成的C程序的可執(zhí)行文件。
總結(jié)
生成可執(zhí)行程序過程為成四個(gè)步驟:
- 由.c文件到.i文件,這個(gè)過程叫預(yù)處理。
- 由.i文件到.s文件,這個(gè)過程叫編譯。
- 由.s文件到.o文件,這個(gè)過程叫匯編。
- 由.o文件到可執(zhí)行文件,這個(gè)過程叫鏈接。
在集成開發(fā)環(huán)境中,點(diǎn)擊"編譯"按鈕后,IDE會(huì)自動(dòng)完成上述四個(gè)階段,無需手動(dòng)執(zhí)行每個(gè)步驟。如果沒有編譯錯(cuò)誤,最終的可執(zhí)行文件將生成并可以在IDE中直接運(yùn)行。文章來源:http://www.zghlxwxcb.cn/news/detail-636460.html
雖然IDE為我們提供了方便的編譯工具,但了解C語言的編譯過程仍然對(duì)于程序員來說是重要的,特別是在解決一些編譯錯(cuò)誤或進(jìn)行優(yōu)化時(shí),理解底層過程可以幫助我們更好地理解和改進(jìn)代碼。文章來源地址http://www.zghlxwxcb.cn/news/detail-636460.html
到了這里,關(guān)于GCC編譯過程:預(yù)處理->編譯->匯編->鏈接的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!