Linux 基礎(chǔ)教程 - 使用 GCC 編譯器編譯二進(jìn)制

GCC 編譯器介紹

GCC 是一種免費(fèi)的開(kāi)源編譯器,適用于多種編程語(yǔ)言,包括 C、C++、Objective-C、Fortran、Ada 等。開(kāi)源優(yōu)勢(shì)意味任何人都可以根據(jù)自己的需要自由修改和分發(fā) GCC 的源代碼,除開(kāi)源以外,GCC 還有以下幾個(gè)優(yōu)勢(shì):

  • 跨平臺(tái)兼容性
    GCC 可以在各種操作系統(tǒng)上運(yùn)行,包括 Linux、macOS 和 Windows。 這可以大幅度提高不同系統(tǒng)上的工作協(xié)同效率。

  • 模塊化架構(gòu)
    GCC 被設(shè)計(jì)為一個(gè)模塊化系統(tǒng),不同的組件可以根據(jù)用戶的要求進(jìn)行組合或替換。 這種模塊化允許 GCC 支持多種語(yǔ)言和體系結(jié)構(gòu),還允許用戶根據(jù)自己的需要添加或刪除功能。

  • 優(yōu)化代碼生成效率
    GCC 通過(guò)了循環(huán)展開(kāi)、指令調(diào)度和分支預(yù)測(cè)等多種技術(shù)實(shí)現(xiàn)代碼生成優(yōu)化,使其生成運(yùn)行速度更快且使用更少系統(tǒng)資源的優(yōu)化代碼。

  • 遵從行業(yè)標(biāo)準(zhǔn):
    GCC 符合各種行業(yè)標(biāo)準(zhǔn),例如 C99 和 C++11 標(biāo)準(zhǔn),使其成為 C 和 C++ 開(kāi)發(fā)的可靠且廣泛接受的選擇。 GCC 還支持對(duì)這些標(biāo)準(zhǔn)的各種擴(kuò)展,允許開(kāi)發(fā)人員編寫(xiě)特定于他們要求的代碼。

  • 社區(qū)支持
    GCC 擁有龐大而活躍的開(kāi)發(fā)人員和用戶社區(qū),他們?yōu)槠溟_(kāi)發(fā)和維護(hù)做出貢獻(xiàn)。 該社區(qū)為用戶提供支持和資源,使開(kāi)發(fā)人員更容易使用 GCC 并在需要時(shí)獲得幫助。

  1. Linux 開(kāi)發(fā)C/C++ 一定要熟悉 GCC
  2. VSCode 是通過(guò)調(diào)用 GCC 編譯器來(lái)實(shí)現(xiàn) C/C++ 的編譯工作的;
    實(shí)際使用中:
    使用 gcc 指令編譯 C 代碼
    使用 g++指令編譯 C++ 代碼

編譯過(guò)程簡(jiǎn)析

  1. 預(yù)處理 - Pre-Processing // i文件
    -E 選項(xiàng)指示編譯器僅對(duì)輸入文件進(jìn)行預(yù)處理
g++ -E main.cpp -o main.i // i文件

2。 編譯 - Compiling // s 文件
-S 編譯選項(xiàng)告訴 g++ 在為 C++ 代碼產(chǎn)生了匯編語(yǔ)言文件后停止編譯,g++ 產(chǎn)生的匯編語(yǔ)言文件的缺省擴(kuò)展名是 .s

g++ -S main.i -o main.s

3。 匯編 - Assembling // .o文件
-c 選項(xiàng)告訴 g++ 僅把源代碼編譯為機(jī)器語(yǔ)言的目標(biāo)代碼,缺省時(shí) g++ 建立的目標(biāo)代碼文件有一個(gè) .o 的擴(kuò)展名

g++ -c main.s -o main.o

4。 鏈接 - Linking // bin 文件
-o 編譯選項(xiàng)來(lái)為將產(chǎn)生的可執(zhí)行文件用指定的文件名

g++ main.o -o main

5。 以上可以綜合為

g++ main.cpp -o main

g++ 重要編譯參數(shù)

-g 編譯帶調(diào)試信息的可執(zhí)行文件

-g 選項(xiàng)告訴 GCC 產(chǎn)生能被 GNU 調(diào)試器 GDB 使用的調(diào)試信息,以調(diào)試程序。
產(chǎn)生帶調(diào)試信息的可執(zhí)行文件 main

g++ -g main.cpp

-O[n] 優(yōu)化源代碼

所謂優(yōu)化,例如省略掉代碼中從未使用過(guò)的變量、直接將常量表達(dá)式用結(jié)果值代替等等,這些操作會(huì)使編譯的速度降低,同時(shí)縮減目標(biāo)文件所包含的代碼量,提高最終生成的可執(zhí)行文件的運(yùn)行效率。

  • -O 選項(xiàng)告訴 g++ 對(duì)源代碼進(jìn)行基本優(yōu)化。 這些優(yōu)化在大多數(shù)情況下都會(huì)使程序執(zhí)行的更快,同時(shí)減小代碼的長(zhǎng)度和執(zhí)行時(shí)間,其效果等價(jià)于-O1
  • -O0 表示不做優(yōu)化
  • -O1 為默認(rèn)優(yōu)化
  • -O2 除了完成 -O1 的優(yōu)化之外,還進(jìn)行一些額外的調(diào)整工作,如指令調(diào)整等。
  • -O3 則包括循環(huán)展開(kāi)和其他一些與處理特性相關(guān)的優(yōu)化工作。
    使用 -O2優(yōu)化源代碼,并輸出可執(zhí)行文件
g++ -O2 main.cpp

-l 和 -L 指定庫(kù)文件 | 指定庫(kù)文件路徑

-l 參數(shù)(小寫(xiě))就是用來(lái)指定程序要鏈接的庫(kù),-l 參數(shù)緊接著就是庫(kù)名在 /lib/usr/lib/usr/local/lib 里的庫(kù)直接用 -l 參數(shù)就能鏈接,比如鏈接 glog 庫(kù)。

g++ -lglog main.cpp

如果庫(kù)文件沒(méi)放在上面三個(gè)目錄里,需要使用 -L 參數(shù)(大寫(xiě))指定庫(kù)文件所在目錄,-L 參數(shù)跟著的是庫(kù)文件所在的目錄名,鏈接 mytest 庫(kù),/home/bing/mytestlibfolder/libmytest.so。

g++ -L/home/bing/mytestlibfolder -lmytest main.cpp

-I 指定頭文件搜索目錄

/usr/include 目錄一般是不用指定的,gcc 知道去那里找,但是如果頭文件不在 /usr/icnclude
里我們就要用 -I 參數(shù)指定了,比如頭文件放在 /myinclude 目錄里,那編譯命令行就要加上 -I/myinclude 參數(shù)了,如果不加你會(huì)得到一個(gè)"xxxx.h: No such file or directory" 的錯(cuò)誤。 -I 參數(shù)可以用相對(duì)路徑,比如頭文件在當(dāng)前目錄,可以用-I。來(lái)指定。 上面我們提到的 –cflags 參數(shù)就是用來(lái)生成 -I 參數(shù)的。
g++ -I/myinclude main.cpp

-Wall 打印警告信息

打印出 gcc 提供的警告信息

g++ -Wall main.cpp

-w 關(guān)閉警告信息

g++ -w main.cpp

-std=c++11 設(shè)置編譯標(biāo)準(zhǔn)

使用 c++11 標(biāo)準(zhǔn)編譯 main.cpp

g++ -std=c++11 main.cpp

-o 指定輸出文件名

指定即將產(chǎn)生的文件名

g++ main.cpp -o niam

-D 定義宏

在使用 gcc/g++ 編譯的時(shí)候定義宏
常用場(chǎng)景: -DDEBUG 定義 DEBUG 宏,可能文件中有 DEBUG 宏部分的相關(guān)信息,用個(gè) DDEBUG來(lái) 選擇開(kāi)啟或關(guān)閉

#include <iostream>

int main()
{
#ifdef DEBUG
    std::cout << "DEBUG" << std::endl;
#endif
    // system("pause");
    return 0;
}
g++ -DDEBUG main.cpp -o niam

注: 使用 man gcc 命令可以查看gcc英文使用手冊(cè)

用例

最初目錄結(jié)構(gòu)

.
├── include
│   └── StaticPolymorphism.h
├── main.cpp
└── src
   └── StaticPolymorphism.cpp

2 directories,3 files

直接編譯

  • 最簡(jiǎn)單的編譯,并運(yùn)行
# 將 main.cpp src/StaticPolymorphism.cpp 編譯為可執(zhí)行文件
g++ -Iinc main.cpp src/StaticPolymorphism.cpp
# 運(yùn)行 a.out
time ./a.out
  • 增加參數(shù)編譯,并運(yùn)行
# 將 main.cpp src/StaticPolymorphism.cpp 編譯為可執(zhí)行文件 附帶一堆參數(shù)
g++ -g -std=c++11 -O2 -Wall -Iinc main.cpp src/StaticPolymorphism.cpp -o main
# 運(yùn)行 main
time ./main

鏈接靜態(tài)庫(kù)生成可執(zhí)行文件

# 進(jìn)入src目錄下
cd src

# 匯編,生成 StaticPolymorphism.o 文件
g++ StaticPolymorphism.cpp -c -I../inc

# 生成靜態(tài)庫(kù) libStaticPolymorphism.a
ar rs libStaticPolymorphism.a StaticPolymorphism.o

# 回到上級(jí)目錄
cd ..

# 鏈接,生成可執(zhí)行文件: main-with-static-link
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-static-link

詳細(xì)流程

  • 使用 g++ -c 命令 cpp 源文件編譯成可重定位目標(biāo)文件(.o文件)
g++ StaticPolymorphism.cpp -c -I../inc
  • 使用 ar rs 命令將可重定位目標(biāo)文件打包成靜態(tài)庫(kù)。
    庫(kù)文件名都是以 lib 開(kāi)頭的,靜態(tài)庫(kù)以 .a 作為后綴,表示 Archive。 ar 命令將目標(biāo)文件打包成靜態(tài)庫(kù),選項(xiàng) r 表示將后面的文件列表添加到文件包,如果文件包不存在就創(chuàng)建它,如果文件包中已有同名文件就替換成新的。 選項(xiàng) s 是專(zhuān)用于生成靜態(tài)庫(kù)的,表示為靜態(tài)庫(kù)創(chuàng)建索引,這個(gè)索引被鏈接器使用。
ar rs libStaticPolymorphism.a StaticPolymorphism.o
  • 把靜態(tài)庫(kù)和 main.cpp 編譯鏈接在一起。 例如
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-static-link

其中,-L 選項(xiàng)告訴編譯器去哪里找需要的庫(kù)文件,-lStaticPolymorphism 告訴編譯器要鏈接 libStaticPolymorphism 庫(kù), -I 選項(xiàng)告訴編譯器去哪里找頭文件。 注意,即使庫(kù)文件就在當(dāng)前目錄,編譯器默認(rèn)也不會(huì)去找的,所以 -L。 選項(xiàng)不能少。 編譯器默認(rèn)的查找目錄可以用-print-search-dirs 選項(xiàng)查看。

編譯器會(huì)在默認(rèn)搜索路徑以及 -L 選項(xiàng)指定的路徑中查找用 -l 選項(xiàng)指定的庫(kù),比如 -lStaticPolymorphism,編譯器會(huì)首先找有沒(méi)有動(dòng)態(tài)庫(kù) libStaticPolymorphism.so,如果有就鏈接它,如果沒(méi)有就找有沒(méi)有靜態(tài)庫(kù) libStaticPolymorphism.a,如果有就鏈接它。 所以編譯器是優(yōu)先考慮動(dòng)態(tài)庫(kù)的,如果希望編譯器本次編譯只鏈接靜態(tài)庫(kù),可以指定 -static 選項(xiàng)。

在鏈接靜態(tài)庫(kù)時(shí),鏈接器會(huì)把靜態(tài)庫(kù)中的目標(biāo)文件取出來(lái)和可執(zhí)行文件真正鏈接在一起。 鏈接器可以從靜態(tài)庫(kù)中只取出需要的目標(biāo)文件來(lái)做鏈接。 而且使用靜態(tài)庫(kù)只需寫(xiě)一個(gè)庫(kù)文件名,而不需要寫(xiě)一長(zhǎng)串目標(biāo)文件名。

最終目錄結(jié)構(gòu)

.
├── inc
│   └── StaticPolymorphism.h
├── main.cpp
├── main-with-static-link
├── README.md
└── src
    ├── libStaticPolymorphism.a
    ├── StaticPolymorphism.cpp
    └── StaticPolymorphism.o

運(yùn)行可執(zhí)行文件

./main-with-static-link

鏈接動(dòng)態(tài)庫(kù)生成可執(zhí)行文件

# 進(jìn)入src目錄下
cd src

# 生成動(dòng)態(tài)庫(kù) libStaticPolymorphism.so
g++ StaticPolymorphism.cpp -I../inc -fPIC -shared -o libStaticPolymorphism.so

# 上面命令等價(jià)于以下兩條命令
g++ StaticPolymorphism.cpp -I../inc -c -fPIC
g++ -shared -o libStaticPolymorphism.so StaticPolymorphism.o

# 回到上級(jí)目錄
cd ..

# 鏈接,生成可執(zhí)行文件: main-with-dynamic-link
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-dynamic-link

詳細(xì)流程

  • 使用 g++ -c -fPIC 命令 將 cpp 源文件編譯成可重定位目標(biāo)文件。 組成動(dòng)態(tài)庫(kù)的目標(biāo)文件和一般的目標(biāo)文件有所不同,在編譯時(shí)要加 -fPIC 選項(xiàng)
g++ StaticPolymorphism.cpp -I../inc -c -fPIC
  • 使用 g++ -shared -o 命令將目標(biāo)文件編譯成動(dòng)態(tài)庫(kù)。
g++ -shared -o libStaticPolymorphism.so StaticPolymorphism.o
  • 把 main.c 和動(dòng)態(tài)庫(kù)編譯鏈接在一起,例如:
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-dynamic-link

使用動(dòng)態(tài)庫(kù)編譯鏈接生成的可執(zhí)行文件只是包含動(dòng)態(tài)庫(kù)信息,并沒(méi)有真的做動(dòng)態(tài)鏈接。 在可執(zhí)行文件加載到內(nèi)存時(shí),根據(jù)程序包含的動(dòng)態(tài)庫(kù)信息做動(dòng)態(tài)鏈接。
運(yùn)行可執(zhí)行文件main可以會(huì)報(bào)錯(cuò):

./main-with-dynamic-link: error while loading shared libraries: libStaticPolymorphism.so: cannot open shared object file: No such file or directory

編譯的時(shí)候沒(méi)問(wèn)題,由于指定了 -Lsrc 選項(xiàng),編譯器可以在 src 目錄下找到 libstack.so,而運(yùn)行時(shí)卻說(shuō)找不到 libstack.so。 可以使用 ldd 命令查看可執(zhí)行文件依賴(lài)于哪些動(dòng)態(tài)庫(kù)。

ldd main-with-dynamic-link
linux-vdso.so.1 (0x00007fffe9fbb000)
libStaticPolymorphism.so => not found
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f51da000000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f51d9c00000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f51da263000)
/lib64/ld-linux-x86-64.so。2 (0x00007f51da362000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f51da243000)

解決方法:

  1. 把 libStaticPolymorphism.so 所在目錄的絕對(duì)路徑 (比如/home/klein/compiler-case/compilation-dynamic-link/src) 添加到 /etc/ld.so.conf 中(該文件中每個(gè)路徑占一行); 然后運(yùn)行l(wèi)dconfig命令。 ldconfig 命令會(huì)處理 /etc/ld.so.conf 中配置的目錄和一些系統(tǒng)默認(rèn)目錄,如/lib,/usr/lib等,處理之后生成 /etc/ld.so.cache 緩存文件,動(dòng)態(tài)鏈接器就從這個(gè)緩存中搜索動(dòng)態(tài)庫(kù)。
  2. 把 libStaticPolymorphism.so 拷到 /usr/lib 或 /lib 目錄,這樣可以確保動(dòng)態(tài)鏈接器能找到這個(gè)動(dòng)態(tài)庫(kù)。
  3. 使用 LD_LIBRARY_PATH 環(huán)境變量預(yù)先加載 libStaticPolymorphism.so 所在目錄。

最終目錄結(jié)構(gòu)

.
├── inc
│   └── StaticPolymorphism.h
├── main.cpp
├── main-with-dynamic-link
├── README.md
└── src
    ├── libStaticPolymorphism.so
    ├── StaticPolymorphism.cpp
    └── StaticPolymorphism.o

2 directories,7 files

運(yùn)行可執(zhí)行文件

LD_LIBRARY_PATH=src ./main-with-dynamic-link
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容