日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

GCC 编译 C++ 程序分步骤流程(预处理 gcc -E、编译 gcc -S、汇编 gcc -c 和链接 gcc 以及 gcc -o 选项)

發(fā)布時(shí)間:2023/11/28 生活经验 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GCC 编译 C++ 程序分步骤流程(预处理 gcc -E、编译 gcc -S、汇编 gcc -c 和链接 gcc 以及 gcc -o 选项) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

C 或者 C++ 程序從源代碼生成可執(zhí)行程序的過程,需經(jīng)歷 4 個(gè)過程,分別是預(yù)處理、編譯、匯編和鏈接。

同樣,使用 GCC 編譯器編譯 C 或者 C++ 程序,也必須要經(jīng)歷這 4 個(gè)過程。但考慮在實(shí)際使用中,用戶可能并不關(guān)心程序的執(zhí)行結(jié)果,只想快速得到最終的可執(zhí)行程序,因此 gccg++ 都對(duì)此需求做了支持。

#include <iostream>int main()
{std::cout << "hello,world" << std::endl;return 0;
}

編寫如下指令并執(zhí)行


wohu@ubuntu:~/cpp/src$ g++ test.cpp 
wohu@ubuntu:~/cpp/src$ ls
a.out  test.cpp

同樣,GCC 編譯器會(huì)在當(dāng)前目錄下生成一個(gè)名為 a.out 的可執(zhí)行文件(如果之前有同名文件,舊文件會(huì)被覆蓋)。通過如下指令即可運(yùn)行該文件:

wohu@ubuntu:~/cpp/src$ ./a.out 
hello,world
wohu@ubuntu:~/cpp/src$

注意,gcc 或者 g++ 指令還支持用戶手動(dòng)指定最終生成的可執(zhí)行文件的文件名,

wohu@ubuntu:~/cpp/src$ g++ test.cpp  -o main
wohu@ubuntu:~/cpp/src$ ls
a.out  main  test.cpp
wohu@ubuntu:~/cpp/src$ ./main 
hello,world
wohu@ubuntu:~/cpp/src$ 

其中 -o 選項(xiàng)用于指定要生成的文件名,例如 -o main 即表示將生成的可執(zhí)行文件名設(shè)為 main

1. gcc -o選項(xiàng)

gcc -o 選項(xiàng)用來指定輸出文件,如果不使用 -o 選項(xiàng),那么將采用默認(rèn)的輸出文件。例如默認(rèn)情況下,生成的可執(zhí)行文件的名字默認(rèn)為 a.out

如下是 gcc -o 指令的使用語法格式:

gcc [-E|-S|-c] [infile] [-o outfile]

其中,用方括號(hào) [] 括起來的部分可以忽略。

  • [infile] 表示輸入文件(也即要處理的文件),它可以是源文件、匯編文件或者目標(biāo)文件;
  • [outfile] 表示輸出文件(也即處理的結(jié)果),可以是預(yù)處理文件、目標(biāo)文件、可執(zhí)行文件等;

值得一提的是,通常情況下 [infile] 處放置一個(gè)文件,但根據(jù)實(shí)際需要也可以放置多個(gè)文件,表示有多個(gè)輸入文件。

注意,雖然我們僅編寫了一條 gcc 或者 g++ 指令,但其底層依據(jù)是按照預(yù)處理、編譯、匯編、鏈接的過程將 CC++ 程序轉(zhuǎn)變?yōu)榭蓤?zhí)行程序的。而本應(yīng)在預(yù)處理階段、編譯階段、匯編階段生成的中間文件,此執(zhí)行方式默認(rèn)是不會(huì)生成的,只會(huì)生成最終的 a.out 可執(zhí)行文件(除非為 gcc 或者 g++ 額外添加 -save-temps 選項(xiàng))。

查看該過程中產(chǎn)生的中間文件。如此,上面介紹的執(zhí)行方式將不再使用,而要采用分步編譯的方式。

所謂“分步編譯”,即由用戶手動(dòng)調(diào)用 GCC 編譯器完成對(duì) C、C++源代碼的預(yù)處理、編譯、匯編以及鏈接過程,每個(gè)階段都會(huì)生成對(duì)源代碼加工后的文件。

GCC 常用的編譯選項(xiàng)

GCC 手冊(cè)

接下來將對(duì)如何實(shí)現(xiàn)分步編譯做詳細(xì)的講解,即如何將一個(gè)源代碼程序經(jīng)歷預(yù)處理、編譯、匯編以及鏈接這 4 個(gè)過程,最終生成對(duì)應(yīng)的可執(zhí)行程序。

2. 預(yù)處理

無論是 C 還是 C++ 程序,其從源代碼轉(zhuǎn)變?yōu)榭蓤?zhí)行代碼的過程,具體可分為 4 個(gè)過程,分別為預(yù)處理(Preprocessing)、編譯(Compilation)、匯編(Assembly)和鏈接(Linking)。

默認(rèn)情況下,gcc 指令會(huì)一氣呵成,直接將源代碼歷經(jīng)這 4 個(gè)過程轉(zhuǎn)變?yōu)榭蓤?zhí)行代碼,且不會(huì)保留各個(gè)階段產(chǎn)生的中間文件。

而如果想查看這 4 個(gè)階段各自產(chǎn)生的中間文件,最簡(jiǎn)單直接的方式就是對(duì)源代碼進(jìn)行“分步編譯”,即控制 GCC 編譯器逐步對(duì)源代碼進(jìn)行預(yù)處理、編譯、匯編以及鏈接操作。其中,通過為 gcc 指令添加 -E 選項(xiàng),即可控制 GCC 編譯器僅對(duì)源代碼做預(yù)處理操作。

所謂預(yù)處理操作,主要是處理那些源文件和頭文件中以 # 開頭的命令(比如 #include、#define、#ifdef 等),并刪除程序中所有的注釋 // 和 /* … */

默認(rèn)情況下 gcc -E 指令只會(huì)將預(yù)處理操作的結(jié)果輸出到屏幕上,并不會(huì)自動(dòng)保存到某個(gè)文件。因此該指令往往會(huì)和 -o 選項(xiàng)連用,將結(jié)果導(dǎo)入到指令的文件中。比如:

wohu@ubuntu:~/cpp/src$ g++ -E test.cpp -o test.ii
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.ii
wohu@ubuntu:~/cpp/src$ 

Linux 系統(tǒng)中,通常用 .i 或者 .ii 作為 C/C++ 程序預(yù)處理后所得文件的后綴名。由此,就完成了 test.cpp 文件的預(yù)處理操作,并將其結(jié)果導(dǎo)入到了 test.ii 文件中。

讀者可以通過執(zhí)行 cat test.ii 指令查看該文件中的內(nèi)容,但通常沒有足夠 C++ 語言功底的讀者是看不懂的。為此,我們可以為 g++ 指令再添加一個(gè) -C 選項(xiàng),阻止 GCC 刪除源文件和頭文件中的注釋:

wohu@ubuntu:~/cpp/src$ g++ -E -C test.cpp -o test.ii

注意,這里是大寫的 -C,不是小寫的 -c。小寫的 -c 另作他用,

gcc -E 支持的常用選項(xiàng)

其中,對(duì)于指定 #include 搜索路徑的幾個(gè)選項(xiàng),作用的先后順序如下:

  • 對(duì)于用 #include "" 引號(hào)形式引入的頭文件,首先搜索當(dāng)前程序文件所在的目錄,其次再前往 -iquote 選項(xiàng)指定的目錄中查找;
  • 前往 -I 選項(xiàng)指定的目錄中搜索;
  • 前往 -isystem 選項(xiàng)指定的目錄中搜索;
  • 前往默認(rèn)的系統(tǒng)路徑下搜索;
  • 前往 -idirafter 選項(xiàng)指定的目錄中搜索;

3. 編譯

所謂編譯,簡(jiǎn)單理解就是將預(yù)處理得到的程序代碼,經(jīng)過一系列的詞法分析、語法分析、語義分析以及優(yōu)化,加工為當(dāng)前機(jī)器支持的匯編代碼。

通過給 g++ 指令添加 -S(注意是大寫)選項(xiàng),即可令 GCC 編譯器僅將指定文件加工至編譯階段,并生成對(duì)應(yīng)的匯編代碼文件。例如:

wohu@ubuntu:~/cpp/src$ g++ -S test.ii
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.ii  test.s
wohu@ubuntu:~/cpp/src$ 

可以看到,經(jīng)過執(zhí)行 g++ -S 指令,其生成了一個(gè)名為 test.s 的文件,這就是經(jīng)過編譯的匯編代碼文件。也就是說默認(rèn)情況下,編譯操作會(huì)自行新建一個(gè)文件名和指定文件相同、后綴名為 .s 的文件,并將編譯的結(jié)果保存在該文件中。

我們還可以為 gcc -S 指令添加 -o 選項(xiàng),令 GCC 編譯器將編譯結(jié)果保存在我們指定的文件中。例如:

wohu@ubuntu:~/cpp/src$ g++ -S test.ii -o demo.s
wohu@ubuntu:~/cpp/src$ ls
demo.s  test.cpp  test.ii  test.s
wohu@ubuntu:~/cpp/src$ 

需要注意的是,gcc -S 指令操作的文件并非必須是經(jīng)過預(yù)處理后得到的 .i.ii 文件,-S 選項(xiàng)的功能是令 GCC 編譯器將指定文件處理至編譯階段結(jié)束。這也就意味著,gcc -S 指令可以操作預(yù)處理后的 .i.ii 文件,也可以操作源代碼文件:

  • 如果操作對(duì)象為 .i.ii 文件,則 GCC 編譯器只需編譯此文件;
  • 如果操作對(duì)象為 .c 或者 .cpp 源代碼文件,則 GCC 編譯器會(huì)對(duì)其進(jìn)行預(yù)處理和編譯這 2 步操作。

因此,如果我們想直接得到 test.cpp 文件對(duì)應(yīng)的匯編文件,就可以借助 gcc -S 指令:

wohu@ubuntu:~/cpp/src$ g++ -S test.cpp -o test.s
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.s
wohu@ubuntu:~/cpp/src$ 

由此,我們就可以直接獲得 test.cpp 對(duì)應(yīng)的 test.s 匯編文件。

如果想提高文件內(nèi)匯編代碼的可讀性,可以借助 -fverbose-asm 選項(xiàng),GCC 編譯器會(huì)自行為匯編代碼添加必要的注釋,例如:

wohu@ubuntu:~/cpp/src$ g++ -S test.cpp -o test.s -fverbose-asm
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.s
wohu@ubuntu:~/cpp/src$ 

4. 匯編

如何對(duì)已得到的 test.s 執(zhí)行匯編操作,并得到相應(yīng)的目標(biāo)文件,需要進(jìn)行匯編操作。所謂目標(biāo)文件,其本質(zhì)為二進(jìn)制文件,但由于尚未經(jīng)過鏈接操作,所以無法直接運(yùn)行。

簡(jiǎn)單地理解,匯編其實(shí)就是將匯編代碼轉(zhuǎn)換成可以執(zhí)行的機(jī)器指令。大部分匯編語句對(duì)應(yīng)一條機(jī)器指令,有的匯編語句對(duì)應(yīng)多條機(jī)器指令。相對(duì)于編譯操作,匯編過程會(huì)簡(jiǎn)單很多,它并沒有復(fù)雜的語法,也沒有語義,也不需要做指令優(yōu)化,只需要根據(jù)匯編語句和機(jī)器指令的對(duì)照表一一翻譯即可。

通過為 gcc 指令添加 -c 選項(xiàng)(注意是小寫字母 c),即可讓 GCC 編譯器將指定文件加工至匯編階段,并生成相應(yīng)的目標(biāo)文件。例如:

wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.s
wohu@ubuntu:~/cpp/src$ g++ -c test.s 
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.o  test.s
wohu@ubuntu:~/cpp/src$ 

可以看到,該指令生成了和 test.s 同名但后綴名為 .o 的文件,這就是經(jīng)過匯編操作得到的目標(biāo)文件。

還可以為 gcc -c 指令在添加一個(gè) -o 選項(xiàng),用于將匯編操作的結(jié)果輸入到指定文件中,例如:

wohu@ubuntu:~/cpp/src$ g++ -c test.s  -o test_obj.o
wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.o  test_obj.o  test.s
wohu@ubuntu:~/cpp/src$ 

需要強(qiáng)調(diào)的一點(diǎn)是,和 g++ -S 類似,g++ -c 選項(xiàng)并非只能用于加工 .s 文件。事實(shí)上,-c 選項(xiàng)只是令 GCC 編譯器將指定文件加工至匯編階段,但不執(zhí)行鏈接操作。這也就意味著:

  • 如果指定文件為源程序文件(例如 test.cpp),則 gcc -c 指令會(huì)對(duì) test.cpp 文件執(zhí)行預(yù)處理、編譯以及匯編這 3 步操作;
  • 如果指定文件為剛剛經(jīng)過預(yù)處理后的文件(例如 test.i),則 gcc -c 指令對(duì) test.i 文件執(zhí)行編譯和匯編這 2 步操作;
  • 如果指定文件為剛剛經(jīng)過編譯后的文件(例如 test.s),則 gcc -c 指令只對(duì) test.s 文件執(zhí)行匯編這 1 步操作。

注意,如果指定文件已經(jīng)經(jīng)過匯編,或者 GCC 編譯器無法識(shí)別,則 gcc -c 指令不做任何操作。

5. 鏈接

總的來說鏈接階段要完成的工作,就是將同一項(xiàng)目中各源文件生成的目標(biāo)文件以及程序中用到的庫(kù)文件整合為一個(gè)可執(zhí)行文件。

目標(biāo)文件已經(jīng)是二進(jìn)制文件,與可執(zhí)行文件的組織形式類似,只是有些函數(shù)和全局變量的地址還未找到,因此還無法執(zhí)行。鏈接的作用就是找到這些目標(biāo)地址,將所有的目標(biāo)文件組織成一個(gè)可以執(zhí)行的二進(jìn)制文件。

完成鏈接操作,并不需要給 g++ 添加任何選項(xiàng),只要將匯編階段得到的 test.o 作為參數(shù)傳遞給它,g++就會(huì)在其基礎(chǔ)上完成鏈接操作。例如:

wohu@ubuntu:~/cpp/src$ ls
test.cpp  test.o  test_obj.o  test.s
wohu@ubuntu:~/cpp/src$ g++ test.o 
wohu@ubuntu:~/cpp/src$ ls
a.out  test.cpp  test.o  test_obj.o  test.s
wohu@ubuntu:~/cpp/src$ g++ test_obj.o -o test_obj
wohu@ubuntu:~/cpp/src$ ls
a.out  test.cpp  test.o  test_obj  test_obj.o  test.s
wohu@ubuntu:~/cpp/src$ ./a.out 
hello,world
wohu@ubuntu:~/cpp/src$ ./test_obj 
hello,world
wohu@ubuntu:~/cpp/src$ 

gcc 會(huì)根據(jù)所給文件的后綴名 .o,自行判斷出此類文件為目標(biāo)文件,僅需要進(jìn)行鏈接操作。
通過分別執(zhí)行這 a.outtest_obj 可執(zhí)行文件,其執(zhí)行結(jié)果完全相同,說明 test.otest_obj.o 是完全相同的。

6. 一步生成所有中間結(jié)果

如果讀者不想執(zhí)行這么多條指令,但想獲得預(yù)處理、編譯、匯編以及鏈接這 4 個(gè)過程產(chǎn)生的中間文件,可以執(zhí)行如下指令:

wohu@ubuntu:~/cpp/src$ ls
test.cpp
wohu@ubuntu:~/cpp/src$ g++ test.cpp -save-temps
wohu@ubuntu:~/cpp/src$ ls
a.out  test.cpp  test.ii  test.o  test.s
wohu@ubuntu:~/cpp/src$ 

可以看到,通過給 g++ 添加 -save-temps 選項(xiàng),可以使 GCC 編譯器保留編譯源文件過程中產(chǎn)生的所有中間文件。

總結(jié)

以上是生活随笔為你收集整理的GCC 编译 C++ 程序分步骤流程(预处理 gcc -E、编译 gcc -S、汇编 gcc -c 和链接 gcc 以及 gcc -o 选项)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。