新聞中心
C 語(yǔ)言廣為人知,深受新老程序員的好評(píng)。使用 C 語(yǔ)言編寫的源文件代碼,使用了標(biāo)準(zhǔn)的英語(yǔ)術(shù)語(yǔ),因而人們可以方便閱讀。然而,計(jì)算機(jī)只能理解二進(jìn)制代碼。為將代碼轉(zhuǎn)換為機(jī)器語(yǔ)言,你需要使用一種被稱為編譯器的工具。

從網(wǎng)站建設(shè)到定制行業(yè)解決方案,為提供做網(wǎng)站、成都網(wǎng)站建設(shè)服務(wù)體系,各種行業(yè)企業(yè)客戶提供網(wǎng)站建設(shè)解決方案,助力業(yè)務(wù)快速發(fā)展。成都創(chuàng)新互聯(lián)將不斷加快創(chuàng)新步伐,提供優(yōu)質(zhì)的建站服務(wù)。
最常見的編譯器是 GCC(GNU 編譯器集)。編譯過程涉及到一系列的中間步驟及相關(guān)工具。
安裝 GCC
為驗(yàn)證在你的系統(tǒng)上是否已經(jīng)安裝了 GCC,使用 gcc 命令:
$ gcc --version
如有必要,使用你的軟件包管理器來(lái)安裝 GCC。在基于 Fedora 的系統(tǒng)上,使用 dnf :
$ sudo dnf install gcc libgcc
在基于 Debian 的系統(tǒng)上,使用 apt :
$ sudo apt install build-essential
在安裝后,如果你想查看 GCC 的安裝位置,那么使用:
$ whereis gcc
演示使用 GCC 來(lái)編譯一個(gè)簡(jiǎn)單的 C 程序
這里有一個(gè)簡(jiǎn)單的 C 程序,用于演示如何使用 GCC 來(lái)編譯。打開你最喜歡的文本編輯器,并在其中粘貼這段代碼:
// hellogcc.c
#include
int main() {
printf("Hello, GCC!\n");
return 0;
}
保存文件為 hellogcc.c ,接下來(lái)編譯它:
$ ls
hellogcc.c
$ gcc hellogcc.c
$ ls -1
a.out
hellogcc.c
如你所見,a.out 是編譯后默認(rèn)生成的二進(jìn)制文件。為查看你所新編譯的應(yīng)用程序的輸出,只需要運(yùn)行它,就像你運(yùn)行任意本地二進(jìn)制文件一樣:
$ ./a.out
Hello, GCC!
命名輸出的文件
文件名稱 a.out 是非常莫名其妙的,所以,如果你想具體指定可執(zhí)行文件的名稱,你可以使用 -o 選項(xiàng):
(LCTT 譯注:注意這和最近 Linux 內(nèi)核廢棄的 a.out 格式無(wú)關(guān),只是名字相同,這里生成的 a.out 是 ELF 格式的 ——
也不知道誰(shuí)給起了個(gè) a.out 這破名字,在我看來(lái),默認(rèn)輸出文件名就應(yīng)該是去掉了 .c 擴(kuò)展名后的名字。by wxy)
$ gcc -o hellogcc hellogcc.c
$ ls
a.out hellogcc hellogcc.c
$ ./hellogcc
Hello, GCC!
當(dāng)開發(fā)一個(gè)需要編譯多個(gè) C 源文件文件的大型應(yīng)用程序時(shí),這種選項(xiàng)是很有用的。
在 GCC 編譯中的中間步驟
編譯實(shí)際上有四個(gè)步驟,即使在簡(jiǎn)單的用例中 GCC 自動(dòng)執(zhí)行了這些步驟。
- 預(yù)處理:GNU 的 C 預(yù)處理器(cpp)解析頭文件(#include 語(yǔ)句),展開 宏macros 定義(#define 語(yǔ)句),并使用展開的源文件代碼來(lái)生成一個(gè)中間文件,如 hellogcc.i。
- 編譯:在這個(gè)期間中,編譯器將預(yù)處理的源文件代碼轉(zhuǎn)換為指定 CPU 架構(gòu)的匯編代碼。由此生成是匯編文件使用一個(gè) .s
擴(kuò)展名來(lái)命名,如在這個(gè)示例中的 hellogcc.s 。 - 匯編:匯編程序(as)將匯編代碼轉(zhuǎn)換為目標(biāo)機(jī)器代碼,放在目標(biāo)文件中,例如 hellogcc.o 。
- 鏈接:鏈接器(ld)將目標(biāo)代碼和庫(kù)代碼鏈接起來(lái)生成一個(gè)可執(zhí)行文件,例如 hellogcc 。
在運(yùn)行 GCC 時(shí),可以使用 -v 選項(xiàng)來(lái)查看每一步的細(xì)節(jié):
$ gcc -v -o hellogcc hellogcc.c
Compiler flowchart
手動(dòng)編譯代碼
體驗(yàn)編譯的每個(gè)步驟可能是很有用的,因此在一些情況下,你不需要 GCC 完成所有的步驟。
首先,除源文件文件以外,刪除在當(dāng)前文件夾下生成的文件。
$ rm a.out hellogcc.o
$ ls
hellogcc.c
預(yù)處理器
首先,啟動(dòng)預(yù)處理器,將其輸出重定向?yàn)?hellogcc.i :
$ cpp hellogcc.c > hellogcc.i
$ ls
hellogcc.c hellogcc.i
查看輸出文件,并注意一下預(yù)處理器是如何包含頭文件和擴(kuò)展宏中的源文件代碼的。
編譯器
現(xiàn)在,你可以編譯代碼為匯編代碼。使用 -S 選項(xiàng)來(lái)設(shè)置 GCC 只生成匯編代碼:
$ gcc -S hellogcc.i
$ ls
hellogcc.c hellogcc.i hellogcc.s
$ cat hellogcc.s
查看匯編代碼,來(lái)看看生成了什么。
匯編
使用你剛剛所生成的匯編代碼來(lái)創(chuàng)建一個(gè)目標(biāo)文件:
$ as -o hellogcc.o hellogcc.s
$ ls
hellogcc.c hellogcc.i hellogcc.o hellogcc.s
鏈接
要生成一個(gè)可執(zhí)行文件,你必須將對(duì)象文件鏈接到它所依賴的庫(kù)。這并不像前面的步驟那么簡(jiǎn)單,但它卻是有教育意義的:
$ ld -o hellogcc hellogcc.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: hellogcc.o: in function `main`:
hellogcc.c:(.text+0xa): undefined reference to `puts'
在鏈接器查找完 libc.so 庫(kù)后,出現(xiàn)一個(gè)引用 undefined puts 錯(cuò)誤。你必須找出適合的鏈接器選項(xiàng)來(lái)鏈接必要的庫(kù)以解決這個(gè)問題。這不是一個(gè)小技巧,它取決于你的系統(tǒng)的布局。
在鏈接時(shí),你必須鏈接代碼到核心運(yùn)行時(shí)(CRT)目標(biāo),這是一組幫助二進(jìn)制可執(zhí)行文件啟動(dòng)的子例程。鏈接器也需要知道在哪里可以找到重要的系統(tǒng)庫(kù),包括 libc 和 libgcc,尤其是其中的特殊的開始和結(jié)束指令。這些指令可以通過 --start-group 和 --end-group 選項(xiàng)來(lái)分隔,或者使用指向 crtbegin.o 和 crtend.o 的路徑。
這個(gè)示例使用了 RHEL 8 上的路徑,因此你可能需要依據(jù)你的系統(tǒng)調(diào)整路徑。
$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
-o hello \
/usr/lib64/crt1.o /usr/lib64/crti.o \
--start-group \
-L/usr/lib/gcc/x86_64-redhat-linux/8 \
-L/usr/lib64 -L/lib64 hello.o \
-lgcc \
--as-needed -lgcc_s \
--no-as-needed -lc -lgcc \
--end-group \
/usr/lib64/crtn.o
在 Slackware 上,同樣的鏈接過程會(huì)使用一組不同的路徑,但是,你可以看到這其中的相似之處:
$ ld -static -o hello \
-L/usr/lib64/gcc/x86_64-slackware-linux/11.2.0/ \
/usr/lib64/crt1.o /usr/lib64/crti.o hello.o /usr/lib64/crtn.o \
--start-group \
-lc -lgcc -lgcc_eh \
--end-group
現(xiàn)在,運(yùn)行由此生成的可執(zhí)行文件:
$ ./hello
Hello, GCC!
一些有用的實(shí)用程序
下面是一些幫助檢查文件類型、符號(hào)表symbol tables 和鏈接到可執(zhí)行文件的庫(kù)的實(shí)用程序。
使用 file 實(shí)用程序可以確定文件的類型:
$ file hellogcc.c
hellogcc.c: C source, ASCII text
$ file hellogcc.o
hellogcc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ file hellogcc
hellogcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb76b241d7d00871806e9fa5e814fee276d5bd1a, for GNU/Linux 3.2.0, not stripped
對(duì)目標(biāo)文件使用 nm 實(shí)用程序可以列出 符號(hào)表 :
$ nm hellogcc.o
0000000000000000 T main
U puts
使用 ldd 實(shí)用程序來(lái)列出動(dòng)態(tài)鏈接庫(kù):
$ ldd hellogcc
linux-vdso.so.1 (0x00007ffe3bdd7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f223395e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2233b7e000)
總結(jié)
在這篇文章中,你了解到了 GCC 編譯中的各種中間步驟,和檢查文件類型、符號(hào)表symbol tables 和鏈接到可執(zhí)行文件的庫(kù)的實(shí)用程序。在你下次使用
GCC 時(shí),你將會(huì)明白它為你生成一個(gè)二進(jìn)制文件所要做的步驟,并且當(dāng)出現(xiàn)一些錯(cuò)誤時(shí),你會(huì)知道如何逐步處理解決問題。
分享文章:GNUC編譯器的程序員入門指南
鏈接分享:http://www.5511xx.com/article/dhhddch.html


咨詢
建站咨詢
