【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

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)行?

主要分為四步:

  1. 預(yù)處理——包括宏替換、去注釋、條件編譯等。
  2. 編譯——生成匯編代碼。
  3. 匯編——生成機(jī)器可識(shí)別代碼。
  4. 連接——生成可執(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:

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

使用vim打開(kāi)文件,并寫(xiě)入以下簡(jiǎn)單代碼:

#include <stdio.h> int main() {     printf("Hello, World!n");     return 0; }

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

寫(xiě)入后,使用以下命令編譯該程序:

gcc hello.c -o hello

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

(hello.c是我們需要編譯的文件,-o是gcc的一個(gè)選項(xiàng),用于指定編譯后的可執(zhí)行文件名,此處我們指定為hello)

編譯完成后,使用ls命令查看當(dāng)前目錄下的文件:

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

我們會(huì)發(fā)現(xiàn)有一個(gè)名為hello的文件,這就是通過(guò)上述命令生成的可執(zhí)行文件。

接下來(lái),使用以下命令運(yùn)行生成的hello可執(zhí)行程序:

./hello

運(yùn)行效果如下:

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

四、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ù):

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

函數(shù)庫(kù)分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)兩種。我們可以使用ldd指令查看一個(gè)可執(zhí)行程序所依賴的動(dòng)態(tài)庫(kù):

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

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文件:

【Linux探索學(xué)習(xí)】第八彈——Linux工具篇(三):Linux 中的編譯器 GCC 的編譯原理和使用詳解

我們可以看到它調(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)贊!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊8 分享