GCC 編譯流程簡介


GCC-GCC編譯流程

序言

對於大多數程序員而言,大家都知道gcc是什么,但是如果不接觸到linux平台下的開發,鮮有人真正了解gcc的編譯流程,因為windows+IDE的開發模式簡直是一條龍全套服務,開發者只需要關系代碼邏輯與功能實現即可,但是,在享受便利的同時,必然也犧牲了一些靈活性。

gcc是什么

國際慣例,先得介紹gcc是什么,gcc的原名為GNU C Compiler,專門針對C語言的編譯器,而在后來計算機的發展中,GCC逐漸兼容了C++,java等語言,發展為擴展版的GCC,全稱為 GNU compiler collection,事實上它是指一套編譯器,而不再是單純的C編譯器,像g++,其實也是屬於GCC工具中的一種。

gcc的基本使用

gcc的編譯過程分多步完成,基本可分為4步:預編譯,編譯,匯編,鏈接,gcc的一般語法為

gcc [-option] <file> [-option] [-dst file]

舉個例子,如果需要編譯一個hello_world.c文件
gcc hello_world.c -o hello_world
在上述例子中,選擇默認的編譯選項,-o 為指定可執行文件的名稱,上述生成hello_world可執行文件,如果不指定可執行文件名稱,默認生成a.out可執行文件。
進入hello_world所在目錄,即可使用./hello_world指令運行程序

常用gcc編譯選項

-E

gcc的-E選項只對目標文件進行預編譯處理

-S

gcc的-S選項只對目標文件進行預編譯處理和編譯處理,這里的編譯處理僅僅是將預編譯處理后的文件編譯成匯編文件

-C

gcc的-C選項對目標文件進行預編譯,編譯,匯編處理,生成二進制目標文件

在默認情況下,gcc編譯時執行預編譯,編譯,匯編和鏈接過程,直接生成可執行代碼

-D

gcc的-D選項相當於在源文件中全局添加一個宏定義,一般在平台兼容或者程序模式切換中比較常見,例如:

#ifdef ARM
    DO SOMETHING
#elif X86
    DO SOMETHING
....
#endif

這時候在編譯時或者在Makefile中加入 -DARM 或者 -DX86 來選擇平台,而不用改源代碼。
又或者,如果你在開發過程中,需要調試某些功能,經常會寫一些調試代碼,而有時候又想屏蔽部分調試代碼,就可以這樣實現:

#ifdef DEBUG_PART1
    DO SOMETHING
#ifdef DEBUG_PART2
    DO SOMETHING 
...
#endif

-O(n)

gcc的-O選項規定了程序的優化等級,分為三個等級,分別是-O,-O2,-O3,所對應的優化等級依次增高.
gcc提供代碼優化功能,可以基本做到同時優化程序空間和運行效率,但是並不是優化等級越高越好,因為優化等級越高就越可能出現一些潛在的風險,因為gcc的優化並不總是正確的,同時可能改變程序邏輯結構,加大調試難度,一個比較普遍的建議是在空間和效率可承受范圍內選最低的優化等級。

-std=XXX

gcc的-std=XXX,這個XXX指定了gcc編譯器的版本,如-std=c99或者-std=c++11,因為在默認編譯條件下,可能不支持某些語言新版本的特性,當程序需要用到高版本語言特性時,則需要指定編譯時對應的語言標准

gcc編譯流程

預編譯

預編譯指將文件中包含的頭文件展開,將所有的宏進行替換,同時將所有的條件編譯解析出來添加到文件中。
所對應的編譯指令為:

gcc -E file >> dst.i

需要注意的是,預編譯並不生成相應的中間文件,直接輸出到終端,因此需要將結果重定向到文件中以便查看。一個簡單的hello_world.c文件經過預編譯的過程將產生800多行的代碼,我將在另一篇博客中詳細討論預編譯中的宏、和條件編譯。

編譯

這個編譯過程是我們通常講的程序編譯的一部分,是將經過預編譯處理的代碼編譯成匯編代碼。
所對應的編譯指令為:

gcc -S file -o dst.s

匯編

匯編過程是將匯編語言的文件編譯成目標文件,即二進制文件,我們通常所使用的靜態庫和動態庫
編譯指令為:

gcc -c file -o dst.o

鏈接

經過匯編生成的目標文件,如果是多個文件的編譯,將會生成多個目標二進制文件,ld連接器將所有的二進制文件鏈接起來,根據平台的不同添加上不同的頭部信息,分配內存空間,需要注意的是,在所有需要鏈接的目標文件中,有且僅有一個main()函數,main()函數作為程序入口函數只能有一個,否則會報錯。
在鏈接過程中,還將涉及到鏈接靜態庫和動態庫的問題。
很多朋友對庫的概念應該不算陌生,經常會用到各種各樣的庫,但是僅僅停留在理解的層次而已,甚至都從來沒有手動管理過庫,其實可以說基本上每個程序員寫的每個程序都要用到庫函數,在C/C++語言中,最明顯的例子就是包含頭文件

#include <stdio.h>

其實,這個包含頭文件的動作就是聲明了glibc中的庫函數,表明我的程序中需要調用到glibc中的庫,而在程序進行鏈接的時候將會將glibc庫鏈接進程序,但是這里有一個動態庫和靜態庫的區分:
靜態庫:直接在鏈接階段就將庫文件鏈接到可執行文件中,最明顯的缺點是可執行文件的尺寸變大。
動態庫:在程序運行時才被載入,相對於靜態庫而言,就有幾個明顯的優點:

  1. 優化了靜態庫的浪費空間問題
  2. 由於只需要在內存中存在一個庫文件,所有程序可以共享,所以在動態庫后續的維護升級中,不必去一個個修改應用程序,而只需要修改庫中的內容,降低耦合性。

gcc鏈接指令

-L 指定鏈接庫的目錄
-l 指定需要鏈接的庫名稱
接下來的博客會詳細講解靜態庫和動態庫的生成以及使用。
(完)

好了,關於GCC編譯流程的問題就到此為止了,如果朋友們對於這個有什么疑問或者發現有文章中有什么錯誤,歡迎留言

原創博客,轉載請注明出處!

祝各位早日實現項目叢中過,bug不沾身.
(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM