深度解析Linux中的編譯器gcc/g++

gc++只用來編譯c語言

g++用來編譯C/c++

程序的翻譯步驟經歷四個過程的

1、預處理(進行宏替換/去注釋/條件編譯/頭文件展開等)

深度解析Linux中的編譯器gcc/g++

這個-E的意思是從現在開始,進行程序的翻譯,一但預處理做完了,就停下來

那么這里的code.i保存的是預處理之后的結果

我們這里的-o選項就是指明了我們的生成文件的名稱了

那么我們將這個code.i文件和原始的code.c進行對比下

深度解析Linux中的編譯器gcc/g++

對比發現我們的源文件有24行,預處理結算的代碼有800多行

我們在這個階段進行了進行宏替換/去注釋/條件編譯/頭文件展開等等操作

所謂的頭文件展開就是將你要包含的頭文件的相關內容拷貝到我們的源文件里面

2、編譯(生成匯編)

輸入命令gcc -S code.i -o code.s

生成了一個code.s的文件

那么這個文件里面的都是匯編語言了

這個-S的意思即是從現在開始進行程序翻譯,編譯做完,形成匯編,就停下來

3、匯編(生成機器可以識別的代碼)

輸入命令gcc -c code.s -o code.o

-c的意思是開始進行程序的翻譯,匯編完成就停下來

我們生成的code.o文件是不能執行的,并且可以重定位目標二進制文件的

4、鏈接(生成可執行文件或者庫文件)

我們的.o,庫文件進行鏈接,然后就能進行執行的操作

我們可以通過ESc來記住這個前面三個過程的選項

那么分別形成的文件的后綴就是.iso了

如果我們出現這種情況的話,那么就是說明系統覺得我們沒有權限

所以我們是需要進行配置文件的更改操作的

深度解析Linux中的編譯器gcc/g++

我們需要進入到/etc/sudoers這個文件里面,我們需要使用我們的root賬戶,不然得話我們進去之后什么都看不見的

我們使用su切換到管理員然后進入到我們的vim模式

對文件進行修改,我們輸入99gg直接跳轉到第99行就行了

然后我們將root那一行進行復制

然后再粘貼到下面,將我們其他的用戶名輸入進去我們再輸入:wq!就行了

強制保存退出

然后我們的配置文件就修改成功了

那么我們的文件就能進行文件的創建和查看的操作了

為什么我們需要進行匯編呢?

減少語言開發的成本

下面的就是編譯器自舉的操作

深度解析Linux中的編譯器gcc/g++

動靜態庫和動靜態庫鏈接

庫:動態庫、靜態庫

linux中的動態庫的文件—libXXX.so

在Linux中的靜態文件—-libXXX.a

windows中的動態庫—XXXX.dill

在Windows中的動態庫—XXXX.lib

如果我們使用的是gvv -c code.c的話,我們沒有在后面指定上我們的文件名稱的話,那么就會生成同名的.o文件

代碼語言:JavaScript代碼運行次數:0運行復制

[kk@hcss-ecs-28de lesson5]$ gcc -c code.c[kk@hcss-ecs-28de lesson5]$ ll -altotal 16drwxrwxr-x 2 kk kk 4096 Dec 27 14:49 .drwx------ 5 kk kk 4096 Dec 27 14:47 ..-rw-rw-r-- 1 kk kk   69 Dec 27 14:47 code.c-rw-rw-r-- 1 kk kk 1504 Dec 27 14:49 code.o[kk@hcss-ecs-28de lesson5]$ 
深度解析Linux中的編譯器gcc/g++

前面的知識:我們的這個code.o是不能進行執行的操作的,我們是需要進行鏈接的

我們可以再次進行gcc code.o -o mycode的可執行文件

這個操作我們叫做鏈接了

一點開始鏈接了,那么就形成了一個mycode的文件

深度解析Linux中的編譯器gcc/g++

我們使用ldd mycode來進行查看我們是否存在這個鏈接成功了

深度解析Linux中的編譯器gcc/g++

那么我們根據這個鏈接找到的就是這個libc- 2.17.so

這個就是我們的C標準庫

深度解析Linux中的編譯器gcc/g++

gcc會幫你進行c語言的庫的鏈接操作部

代碼語言:javascript代碼運行次數:0運行復制

[kk@hcss-ecs-28de lesson5]$ file mycode mycode: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for gnu/Linux 3.2.0, BuildID[sha1]=8bbc1c98d1da83282c41f5b2eca3a7a97847630a, not stripped[kk@hcss-ecs-28de lesson5]$ 
深度解析Linux中的編譯器gcc/g++

這里我們可以發現我們這里的是動態鏈接

那么我們如何行成靜態鏈接呢?

我們輸入命令gcc code.o -o mycode -Static

那么這個就是采用靜態鏈接的方案

所以gcc默認形成的可執行程序是動態鏈接的

gcc -static:我們要求程序進行靜態鏈接

我們這里如果要使用動態鏈接的話就要使用動態庫

如果要使用靜態鏈接的話就使用靜態庫

深度解析Linux中的編譯器gcc/g++

需要存在對應的庫,我們的Linux默認只存在動態庫

sudo yum install glibc-static libstdc++-static -y我們可以使用這個命令進行靜態庫的安裝操作

深度解析Linux中的編譯器gcc/g++
深度解析Linux中的編譯器gcc/g++

形成的可執行程序里面的static的體積比較大,

動態鏈接就是將庫的地址拷貝到我們的可執行程序里面,動態庫也稱之為共享庫

深度解析Linux中的編譯器gcc/g++

動態庫不能缺失,不然得話會導致所有的程序運行出錯的

靜態鏈接:將我們要的庫方法實現,直接拷貝到我們的可執行程序中

所以這就是被為什么我們采用靜態鏈接時候我們的可執行程序的大小比動態鏈接的時候大了

因為靜態鏈接不再依賴任何庫

我們這里的圖比較形象

將我們視為可執行程序

學校視作內存

網吧視作動態庫

編譯器就是大哥

可執行程序和編譯器產生鏈接,告訴我們動態庫的地址信息

然后我們在內存中執行程序的時候然后執行到上網這一步了

我們直接就調用動態庫了

上完了網吧回學校就是庫函數調用完畢了

假如說你的同學都是可執行程序,他們都會進行調用動態庫的操作,但是這個時候網吧倒閉了

就是動態庫缺失了,那么我們可執行程序的就會出問題了

那么現在我們隨身帶上電腦,就是我們可執行程序直接將我們的庫方法實現,就不用進行庫函數的調用了

那么這個就是靜態操作

直接將可執行程序和庫方法合并起來,內存要大得多

我們可執行程序的時候默認是動態鏈接的,因為可執行文件的體積比較小

動態鏈接的優點:生成的可執行文件比較小

動態鏈接的缺點:一但這個庫丟失了,那么我們所有的可執行文件都運行不了了

靜態鏈接的優點:一但編譯好不依賴任何庫,浪費磁盤和內存空間

在 Linux 系統中,gcc 和 g++ 是 GNU 編譯器集合中的核心工具,分別用于編譯 C 和 C++ 程序。它們支持生成可執行文件,并且能夠鏈接靜態庫和動態庫。以下是關于它們以及動、靜態庫的詳細解析。


一、GCC/G++ 的基本使用1. 編譯階段

源文件編譯為目標文件:

代碼語言:javascript代碼運行次數:0運行復制

gcc -c file.c -o file.og++ -c file.cpp -o file.o

-c 選項表示只進行編譯,不鏈接。生成的 .o 文件是目標文件。2. 鏈接階段

目標文件鏈接為可執行文件:

代碼語言:javascript代碼運行次數:0運行復制

gcc file.o -o outputg++ file.o -o output

3. 編譯并鏈接(一步完成)

直接編譯源文件并生成可執行文件:

代碼語言:javascript代碼運行次數:0運行復制

gcc file.c -o outputg++ file.cpp -o output

二、Linux 動態庫與靜態庫1. 靜態庫

特點:

靜態庫在編譯時被直接嵌入到可執行文件中,生成的二進制文件是獨立的。文件后綴通常為 .a。

創建靜態庫:

代碼語言:javascript代碼運行次數:0運行復制

gcc -c file1.c file2.c  # 編譯生成目標文件ar rcs libmylib.a file1.o file2.o  # 創建靜態庫

ar 是靜態庫管理工具。rcs 參數: r:插入文件到庫中。c:創建庫。s:生成符號索引表。

使用靜態庫:

代碼語言:javascript代碼運行次數:0運行復制

gcc main.c -L. -lmylib -o output

-L.:指定庫所在路徑。-lmylib:鏈接靜態庫 libmylib.a(注意去掉前綴 lib 和后綴 .a)。2. 動態庫

特點:

動態庫在運行時被加載,可執行文件依賴于動態庫文件。文件后綴通常為 .so。動態庫具有節省存儲空間和支持共享的優點。

創建動態庫:

代碼語言:javascript代碼運行次數:0運行復制

gcc -fPIC -c file1.c file2.c  # 編譯為位置無關代碼gcc -shared -o libmylib.so file1.o file2.o  # 創建動態庫

-fPIC:生成位置無關代碼(position Independent Code)。-shared:指定生成共享庫。

使用動態庫:

代碼語言:javascript代碼運行次數:0運行復制

gcc main.c -L. -lmylib -o outputexport LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH  # 指定動態庫路徑./output

LD_LIBRARY_PATH 環境變量指定動態庫的查找路徑。


三、動靜態庫的選擇

對比項

靜態庫

動態庫

文件大小

可執行文件較大,庫內容嵌入其中

可執行文件較小,運行時需要庫文件支持

運行效率

加載速度更快,無需查找庫文件

運行時加載,可能略慢

共享能力

不支持共享,每個程序都包含獨立的副本

支持共享,多個程序可以使用同一個庫

版本控制

更新庫需要重新編譯程序

更新庫無需重新編譯,但需注意兼容性


四、GCC/G++ 動靜態庫的鏈接細節1. 鏈接順序

編譯器按照以下順序查找庫:

指定的路徑(-L 參數)。環境變量 LD_LIBRARY_PATH 中指定的路徑。系統默認路徑 /lib 和 /usr/lib。2. 動態庫優先級

如果同一個路徑下同時存在靜態庫和動態庫,默認優先鏈接動態庫。

強制使用靜態庫:

代碼語言:javascript代碼運行次數:0運行復制

gcc main.c -L. -static -lmylib -o output

-static 選項強制鏈接靜態庫。3. 解決動態庫版本兼容問題

動態庫通常通過符號鏈接來管理版本:

代碼語言:javascript代碼運行次數:0運行復制

libmylib.so -> libmylib.so.1.0libmylib.so.1 -> libmylib.so.1.0

可執行文件加載 libmylib.so,符號鏈接指向具體版本。


五、工具補充

查看目標文件/庫文件的符號表:

代碼語言:javascript代碼運行次數:0運行復制

nm file.onm libmylib.anm libmylib.so

查看動態庫依賴:

代碼語言:javascript代碼運行次數:0運行復制

ldd output

輸出依賴的動態庫列表及路徑。

檢查動態庫是否導出特定符號:

代碼語言:javascript代碼運行次數:0運行復制

objdump -T libmylib.so | grep symbol_name

添加動態庫路徑到系統默認: 將動態庫路徑添加到 /etc/ld.so.conf,然后運行:

代碼語言:javascript代碼運行次數:0運行復制

sudo ldconfig

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享