GCC(gcc) 的不斷發展完善使許多商業編譯器都相形見絀, GCC 由 GNU 創始人 Richard Stallman首創,是 GNU 的標志產品,由于 UNIX 平臺的高度可移植性, GCC 幾乎在各種常見的 UNIX 平臺上都有,即使是 Win32/DOS 也有 GCC 的移植。 比如說 SUN 的 Solaris 操作系統配置的編譯器就是GNU 的 GCC 。 GNU 軟件包括 C 編譯器 GCC , C++ 編譯器 G++ ,匯編器 AS ,鏈接器 LD ,二進制轉換工具(OBJCOPY , OBJDUMP) ,調試工具 (GDB , GDBSERVER , KGDB) 和基于不同硬件平臺的開發庫。 在 GNU GCC 支持下用戶可以使用流行的 C/C++ 語言開發應用程序,滿足生成高效率運行代碼、易掌握的編程語言的用戶需求。 這些工具都是按 GPL 版權聲明發布,任何人可以從網上獲取全部的源代碼,無需使用任何費用。關于 GNU 和公共許可證協議的詳細資料, 讀者可以參看 GNU 網站的介紹, http://www.gnu.org/home.html。 GNU 開發工具都是采用命令行的方式,用戶掌握起來相對比較困難,不如基于 Windows 系統的開發工具好用,但是 GNU 工具的復雜性是由于它更貼近編譯器和操作系統的底層,并提供了更大的靈活性。一旦學習和掌握了相關工具后,就了解了系統設計的基礎知識。 運行于 Linux 操作系統下的自由軟件 GNU gcc 編譯器,不僅可以編譯 Linux 操作系統下運行的應用程序,還可以編譯 Linux 內核本身,甚至可以作交叉編譯,編譯運行于其它 CPU 上的程序。所以,在進行嵌入式系統應用程序開發時,這些工具得到了日益廣泛的應用。 GCC 是 GNU 組織的免費 C 編譯器, Linux 的很多發布缺省安裝的就是這種。很多流行的自由軟件源代碼基本都能在 GCC 編譯器下編譯運行。 所以掌握 GCC 編譯器的使用無論是對于編譯系統內核還是自己的應用程序都是大有好處的。 下面通過一個具體的例子,學習如何使用 GCC 編譯器。 在 Linux 操作系統中,對一個用標準 C 語言寫的源程序進行編譯,要使用 GNU 的 gcc 編譯器。 例如下面一個非常簡單的 Hello 源程序 (hello.c): /******************************************************* * Institute of Automation, Chinese Academy of Sciences * File Name : hello.c * Description : introduce how to compile a source file with gcc * Author : Xueyuan Nie * Date : *******************************************************/ void main() { printf("Hello the world\n") ; } 要編譯這個程序,我們只要在 Linux 的 bash 提示符下輸入命令: $ gcc -o hello hello.c gcc 編譯器就會生成一個 hello 的可執行文件。在 hello.c 的當前目錄下執行 ./hello 就可以看到程序的輸出結果,在屏幕上打印出 “ Hello the world ” 的字符串來。 命令行中 gcc 表示是用 gcc 來編譯源程序; -o outputfilename 選項表示要求編譯器生成文件名為 outputfilename 的可執行文件,如果不指定 -o 選項,則缺省文件名是 a.out 。在這里生成指定文件名為 hello 的可執行文件,而 hello.c 是我們的源程序文件。 gcc 是一個多目標的工具。 gcc 最基本的用法是: gcc [options] file... , 其中的 option 是以 - 開始的各種選項, file 是相關的文件名。在使用 gcc 的時候,必須要給出必要的選項和文件名。 gcc 的整個編譯過程,實質上是分四步進行的,每一步完成一個特定的工作, 這四步分別是:預處理,編譯,匯編和鏈接。它具體完成哪一步,是由 gcc 后面的開關選項和文件類型決定的。 清楚的區別編譯和連接是很重要的。編譯器使用源文件編譯產生某種形式的目標文件 (objectfiles) 。在這個過程中,外部的符號引用并沒有被解釋或替換,然后我們使用鏈接器來鏈接這些目標文件和一些標準的頭文件,最后生成一個可執行文件。在這個過程中,一個目標文件中對別的文件中的符號的引用被解釋,并報告不能被解釋的引用,一般是以錯誤信息的形式報告出來。 gcc 編譯器有許多選項,但對于普通用戶來說只要知道其中常用的幾個就夠了。在這里為讀者列出幾個最常用的選項: -o 選項表示要求編譯器生成指定文件名的可執行文件; -c 選項表示只要求編譯器進行編譯,而不要進行鏈接,生成以源文件的文件名命名但把其后綴由 .c 或 .cc 變成 .o 的目標文件; -g 選項要求編譯器在編譯的時候提供以后對程序進行調試的信息; -E 選項表示編譯器對源文件只進行預處理就停止,而不做編譯,匯編和鏈接; -S 選項表示編譯器只進行編譯,而不做匯編和鏈接; -O 選項是編譯器對程序提供的編譯優化選項,在編譯的時候使用該選項,可以使生成的執行文件的執行效率提高; -Wall 選項指定產生全部的警告信息。 如果你的源代碼中包含有某些函數,則在編譯的時候要鏈接確定的庫,比如代碼中包含了某些數學函數,在 Linux 下,為了使用數學函數,必須和數學庫鏈接,為此要加入 -lm 選項。也許有讀者會問,前面那個例子使用 printf 函數的時候為何沒有鏈接庫呢?在 gcc 中對于一些常用函數的實現, gcc 編譯器會自動去鏈接一些常用庫,這樣用戶就沒有必要自己去指定了。有時候在編譯程序的時候還要指定庫的路徑,這個時候要用到編譯器的 -L 選項指定路徑。比如說我們有一個庫在/home/hoyt/mylib 下,這樣我們編譯的時候還要加上 -L/home/hoyt/mylib 。對于一些標準庫來說,沒有必要指出路徑。只要它們在起缺省庫的路徑下就可以了, gcc 在鏈接的時候會自動找到那些庫的。 GNU 編譯器生成的目標文件缺省格式為 elf(executive linked file) 格式,這是 Linux 系統所采用的可執行鏈接文件的通用文件格式。 elf 格式由若干段 (section) 組成,如果沒有特別指明,由標準 c 源代碼生成的目標文件中包含以下段: .text( 正文段 ) 包含程序的指令代碼, .data( 數據段 ) 包含固定的數據,如常量,字符串等, .bss( 未初始化數據段 ) 包含未初始化的變量和數組等。 讀者若想知道更多的選項及其用法,可以查看 gcc 的幫助文檔,那里有許多對其它選項的詳細說明。 當改變了源文件 hello.c 后,需要重新編譯它: $ gcc -c hello.c 然后重新鏈接生成: $ gcc – o hello.o 對于本例,因為只含有一個源文件,所以當改動了源碼后,進行重新的編譯鏈接的過程顯得并不是太繁瑣,但是,如果在一個工程中包含了若干的源碼文件,而這些源碼文件中的某個或某幾個又被其他源碼文件包含,那么,如果一個文件被改動,則包含它的那些源文件都要進行重新編譯鏈接,工作量是可想而知的。幸運的是, GNU 提供了使這個步驟變得簡單的工具,就是下面要介紹給大家的 GNU Make 工具。 GNU Make make 是負責從項目的源代碼中生成最終可執行文件和其他非源代碼文件的工具。 make 命令本身可帶有四種參數:標志、宏定義、描述文件名和目標文件名。 其標準形式為: make [flags] [macro definitions] [targets] Unix 系統下標志位 flags 選項及其含義為: -f file 指定 file 文件為描述文件,如果 file 參數為 '-' 符,那么描述文件指向標準輸入。如果沒有 '-f' 參數,則系統將默認當前目錄下名為 makefile 或者名為 Makefile 的文件為描述文件。在Linux 中, GNU make 工具在當前工作目錄中按照 GNUmakefile 、 makefile 、 Makefile 的順序搜索makefile 文件。 -i 忽略命令執行返回的出錯信息。 -s 沉默模式,在執行之前不輸出相應的命令行信息。 -r 禁止使用隱含規則。 -n 非執行模式,輸出所有執行命令,但并不執行。 -t 更新目標文件。 -q make 操作將根據目標文件是否已經更新返回 "0" 或非 "0" 的狀態信息。 -p 輸出所有宏定義和目標文件描述。 -d Debug 模式,輸出有關文件和檢測時間的詳細信息。 Linux 下 make 標志位的常用選項與 Unix 系統中稍有不同,下面只列出了不同部分: -c dir 在讀取 makefile 之前改變到指定的目錄 dir 。 -I dir 當包含其他 makefile 文件時,利用該選項指定搜索目錄。 -h help 文擋,顯示所有的 make 選項。 -w 在處理 makefile 之前和之后,都顯示工作目錄。 通過命令行參數中的 target ,可指定 make 要編譯的目標,并且允許同時定義編譯多個目標,操作時按照從左向右的順序依次編譯 target 選項中指定的目標文件。如果命令行中沒有指定目標,則系統默認 target 指向描述文件中第一個目標文件。 make 如何實現對源代碼的操作是通過一個被稱之為 makefile 的文件來完成的,在下面的小節里,主要向讀者介紹一下 makefile 的相關知識。 makefile 基本結構 GNU Make 的主要工作是讀一個文本文件 makefile 。 makefile 是用 bash 語言寫的, bash 語言是很像 BASIC 語言的一種命令解釋語言。這個文件里主要描述了有關哪些目標文件是從哪些依賴文件中產生的,是用何種命令來進行這個產生過程的。有了這些信息, make 會檢查磁盤的文件,如果目標文件的日期 ( 即該文件生成或最后修改的日期 ) 至少比它的一個依賴文件日期早的話, make 就會執行相應的命令,以更新目標文件。 makefile 一般被稱為 “makefile” 或 “Makefile” 。還可以在 make 的命令行中指定別的文件名。如果沒有特別指定的話, make 就會尋找 “makefile” 或 “Makefile” ,所以為了簡單起見,建議讀者使用這兩名字。如果要使用其他文件作為 makefile ,則可利用類似下面的 make 命令選項指定 makefile 文件: $ make -f makefilename 一個 makefile 主要含有一系列的規則,如下: 目標文件名: 依賴文件名 (tab 鍵 ) 命令 第一行稱之為規則,第二行是執行規則的命令,必須要以 tab 鍵開始。 下面舉一個簡單的 makefile 的例子。 executable : main.o io.o gcc main.o io.o -o executable main.o : main.c gcc -Wall -O -g -c main.c -o main.o io.o : io.c gcc -Wall -O -g -c io.c -o io.o 這是一個最簡單的 makefile , make 從第一條規則開始, executable 是 makefile 最終要生成的目標文件。給出的規則說明 executable 依賴于兩個目標文件 main.o 和 io.o ,只要 executable 比它依賴的文件中的任何一個舊的話,下一行的命令就會被執行。但是,在檢查文件 main.o 和 io.o 的日期之前,它會往下查找那些把 main.o 或 io.o 做為目標文件的規則。 make 先找到了關于 main.o 的規則,該目標文件的依賴文件是 main.c 。 makefile 后面的文件中再也找不到生成這個依賴文件的規則了。此時, make 開始檢查磁盤上這個依賴文件的日期,如果這個文件的日期比 main.o 日期新的話,那么這個規則下面的命令 gcc -c main.c – o main.o 就會執行,以更新文件 main.o 。同樣 make 對文件 io.o 做類似的檢查,它的依賴文件是 io.c ,對 io.o 的處理和 main.o 類似。現在, 再回到第一個規則處,如果剛才兩個規則中的任何一個被執行,最終的目標文件executable 都需要重建 ( 因為 executable 所依賴的其中一個 .o 文件就會比它新 ) ,因此鏈接命令就會被執行。 有了 makefile ,對任何一個源文件進行修改后,所有依賴于該文件的目標文件都會被重新編譯 ( 因為 .o 文件依賴于 .c 文件 ) ,進而最終可執行文件會被重新鏈接 ( 因為它所依賴的 .o 文件被改變了 ) ,再也不用手工去一個個修改了。 |