linux下的vim編輯器:【linux探索學(xué)習(xí)】第八彈——linux工具篇(三):linux中的編譯器gcc的編譯原理和使用詳解-csdn博客
前言:
注意:本文是在ubuntu系統(tǒng)下進(jìn)行的操作。
一、什么是GCC GCC是一個(gè)由gnu項(xiàng)目開(kāi)發(fā)的開(kāi)源編譯器,最初僅支持c語(yǔ)言,后擴(kuò)展至c++、Fortran、Ada、Objective-C等多種語(yǔ)言。它是Linux及其他unix系統(tǒng)中廣泛使用的編譯器之一。
二、背景知識(shí) 在正式講解GCC如何編譯及其原理前,我們先回顧一個(gè)在學(xué)習(xí)C語(yǔ)言和C++時(shí)學(xué)過(guò)的背景知識(shí):編寫(xiě)的代碼如何經(jīng)過(guò)編譯器處理,最終生成可執(zhí)行程序并運(yùn)行?
主要分為四步:
- 預(yù)處理——包括宏替換、去注釋、條件編譯等。
- 編譯——生成匯編代碼。
- 匯編——生成機(jī)器可識(shí)別代碼。
- 連接——生成可執(zhí)行文件或庫(kù)文件。
三、GCC的使用 3.1 安裝GCC 在Ubuntu系統(tǒng)中,可以使用以下命令安裝GCC:
sudo apt update sudo apt install build-essential
3.2 基本語(yǔ)法 GCC的基本語(yǔ)法如下:
gcc [options] [source files] [object files] [libraries]
3.3 使用方法 我們通過(guò)一個(gè)C語(yǔ)言代碼示例來(lái)演示GCC的使用。首先創(chuàng)建一個(gè)C語(yǔ)言文件hello.c:
使用vim打開(kāi)文件,并寫(xiě)入以下簡(jiǎn)單代碼:
寫(xiě)入后,使用以下命令編譯該程序:
gcc hello.c -o hello
(hello.c是我們需要編譯的文件,-o是gcc的一個(gè)選項(xiàng),用于指定編譯后的可執(zhí)行文件名,此處我們指定為hello)
編譯完成后,使用ls命令查看當(dāng)前目錄下的文件:
我們會(huì)發(fā)現(xiàn)有一個(gè)名為hello的文件,這就是通過(guò)上述命令生成的可執(zhí)行文件。
接下來(lái),使用以下命令運(yùn)行生成的hello可執(zhí)行程序:
./hello
運(yùn)行效果如下:
四、GCC如何完成編譯 前面我們介紹了如何使用gcc進(jìn)行編譯,現(xiàn)在我們來(lái)探討gcc是如何處理hello.c這樣的C語(yǔ)言文件并生成hello可執(zhí)行程序的。
gcc編譯代碼的過(guò)程也分為四步:
4.1 預(yù)處理 在這個(gè)階段,GCC處理源代碼中的預(yù)處理指令。預(yù)處理器主要完成以下任務(wù):
- 宏替換:將定義的宏(如#define)替換為實(shí)際的值。
- 文件包含:處理#include指令,將被包含的文件內(nèi)容插入到源文件中。
- 條件編譯:根據(jù)條件指令(如#ifdef、#ifndef等)選擇性地編譯代碼。
預(yù)處理結(jié)果是一個(gè)擴(kuò)展名為.i的中間文件,包含了所有宏替換和文件包含后的代碼。
gcc -E hello.c -o hello.i
選項(xiàng)-E的作用是讓gcc在預(yù)處理結(jié)束后停止編譯,生成的.i文件是經(jīng)過(guò)預(yù)處理后的中間代碼。
4.2 編譯 在這個(gè)階段,GCC將預(yù)處理后的源代碼轉(zhuǎn)換為匯編語(yǔ)言。編譯器會(huì)將每個(gè)源文件解析成相應(yīng)的匯編指令。此過(guò)程包括以下幾個(gè)步驟:
- 詞法分析:將源代碼分解成tokens(詞法單元)。
- 語(yǔ)法分析:根據(jù)語(yǔ)言的語(yǔ)法規(guī)則檢查語(yǔ)句的正確性。
- 語(yǔ)義分析:檢查程序的語(yǔ)義,例如變量是否已定義、類型是否匹配等。
編譯結(jié)果是一個(gè)擴(kuò)展名為.s的匯編語(yǔ)言文件。
gcc -S hello.i -o hello.s
4.3 匯編 匯編階段的任務(wù)是將匯編語(yǔ)言代碼轉(zhuǎn)換為機(jī)器碼。GCC使用匯編器(如as)將.s文件轉(zhuǎn)換為目標(biāo)文件(.o文件)。目標(biāo)文件是二進(jìn)制格式,包含了機(jī)器碼和必要的符號(hào)信息。
gcc -c hello.s -o hello.o
4.4 鏈接 最后一步是鏈接。鏈接器(如ld)將一個(gè)或多個(gè)目標(biāo)文件和所需的庫(kù)文件(如標(biāo)準(zhǔn)庫(kù))結(jié)合起來(lái),生成最終的可執(zhí)行文件。鏈接器的主要任務(wù)包括:
- 符號(hào)解析:在目標(biāo)文件之間解決函數(shù)和變量的引用。
- 地址分配:為代碼和數(shù)據(jù)分配內(nèi)存地址。
鏈接結(jié)果是一個(gè)可執(zhí)行的二進(jìn)制文件,通常無(wú)擴(kuò)展名或以.out擴(kuò)展名表示。
gcc hello.o -o hello
鏈接是這幾步中最需詳細(xì)講解的,因?yàn)樗婕暗胶瘮?shù)庫(kù)的概念,下面我們將詳細(xì)探討。
GCC的編譯過(guò)程可以總結(jié)為以下步驟:
- 預(yù)處理:處理宏和頭文件,生成.i文件。
- 編譯:將.i文件轉(zhuǎn)換為.s匯編文件。
- 匯編:將.s文件轉(zhuǎn)換為.o目標(biāo)文件。
- 鏈接:將.o文件和庫(kù)文件鏈接,生成可執(zhí)行文件。
五、函數(shù)庫(kù) 在我們的代碼中,可能會(huì)使用到如printf等函數(shù),這些函數(shù)的實(shí)現(xiàn)并沒(méi)有在代碼中,而是在預(yù)編譯的”stdio.h”中僅有聲明。那么這些函數(shù)的實(shí)現(xiàn)究竟在哪里呢?
實(shí)際上,系統(tǒng)將這些函數(shù)的實(shí)現(xiàn)放置在名為libc.so.6的庫(kù)文件中。gcc在沒(méi)有特別指定時(shí),會(huì)在系統(tǒng)默認(rèn)的搜索路徑“/usr/lib”中查找,并鏈接到libc.so.6庫(kù)函數(shù)中,從而實(shí)現(xiàn)如printf這樣的函數(shù)。這就是鏈接的作用。
我們可以查看路徑“/usr/lib”中所有的函數(shù):
函數(shù)庫(kù)分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)兩種。我們可以使用ldd指令查看一個(gè)可執(zhí)行程序所依賴的動(dòng)態(tài)庫(kù):
gcc在編譯時(shí)默認(rèn)使用動(dòng)態(tài)鏈接,如果需要靜態(tài)鏈接,需要在編譯時(shí)加上-Static選項(xiàng)。
動(dòng)態(tài)鏈接示例:
gcc test.c -o mytest
靜態(tài)鏈接示例:
gcc test.c -o mytest -static
在實(shí)際使用中,可能會(huì)混合使用動(dòng)態(tài)和靜態(tài)鏈接。
我們可以使用file指令查看所調(diào)用的庫(kù)類型,命令如下:
file 可執(zhí)行文件名
例如,對(duì)于上面的hello文件:
我們可以看到它調(diào)用的是以.so結(jié)尾的動(dòng)態(tài)庫(kù)。
六、常用選項(xiàng) GCC提供了多種選項(xiàng),以滿足不同的需求。以下是一些常用的選項(xiàng):
- -o
: 指定輸出文件名。 - -Wall: 開(kāi)啟所有警告信息。
- -g: 生成調(diào)試信息,用于調(diào)試程序。
- -O
: 優(yōu)化級(jí)別,-O0(無(wú)優(yōu)化)、-O1(基本優(yōu)化)、-O2(較高優(yōu)化)、-O3(最高優(yōu)化)。 - -c: 僅編譯源代碼,不進(jìn)行鏈接,生成目標(biāo)文件(.o)。
示例:
生成調(diào)試信息:
gcc -g hello.c -o hello
開(kāi)啟所有警告信息:
gcc -Wall hello.c -o hello
進(jìn)行優(yōu)化編譯:
gcc -O2 hello.c -o hello
七、總結(jié) 感謝大家的閱讀,創(chuàng)作不易,望各位支持和點(diǎn)贊!