天天看點

《嵌入式 Linux C 語言應用程式設計(修訂版)》——2.3 嵌入式Linux編譯器GCC的使用ifndef HELLO_H define HELLO_H endifinclude include include "hello.h"

本節書摘來異步社群《嵌入式 linux c 語言應用程式設計(修訂版)》一書中的第2章,第2.3節,作者:華清遠見嵌入式教育訓練中心,孫瓊,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

嵌入式 linux c 語言應用程式設計(修訂版)

作為自由軟體的旗艦項目,richard stallman在十多年前剛開始寫作gcc的時候,還隻是僅僅把它當作一個c程式語言的編譯器,gcc的意思也隻是gnu c compiler而已。

經過了這麼多年的發展,gcc已經不僅僅能支援c語言,它現在還支援ada語言、c++語言、java語言、objective c語言、pascal語言、cobol語言,并支援函數式程式設計和邏輯程式設計的mercury語言等。而gcc也不再單隻gnu c語言編譯器的意思了,而是變成了gnu編譯器家族了。

正如前文中所述,gcc的編譯流程分為了4個步驟,分别為。

預處理(pre-processing)。

編譯(compiling)。

彙編(assembling)。

連結(linking)。

編譯器通過程式的擴充名可分辨編寫原始程式碼所用的語言,由于不同的程式所需要執行編譯的步驟是不同的,是以gcc根據不同的字尾名對它們進行分别處理,表2.6指出了不同字尾名的處理方式。

《嵌入式 Linux C 語言應用程式設計(修訂版)》——2.3 嵌入式Linux編譯器GCC的使用ifndef HELLO_H define HELLO_H endifinclude include include "hello.h"

gcc使用的基本文法為:

/hello.h/

typedef unsigned long val32_t;

/hello.c/

int main()

{

}<code>`</code>

1.預處理階段

gcc的選項“-e”可以使編譯器在預處理結束時就停止編譯,選項“-o”是指定gcc輸出的結果,其指令格式為如下所示。

[root@localhost gcc]# gcc –e –o hello.i hello.c<code>`</code>

在此處,選項‘-o’是指目标檔案,由2.6表可知,‘.i’檔案為已經過預處理的c原始程式。以下列出了hello.i檔案的部分内容。

[root@localhost gcc]# gcc –s –o hello.s hello.i<code>`</code>

以下列出了hello.s的内容,可見gcc已經将其轉化為彙編了,感興趣的讀者可以分析一下這一行簡單的c語言小程式用彙編代碼是如何實作的。

[root@localhost gcc]# gcc –c hello.s –o hello.o<code>`</code>

4.連結階段

在成功編譯之後,就進入了連結階段。在這裡涉及一個重要的概念:函數庫。

在這個程式中并沒有定義“printf”的函數實作,在預編譯中包含進的“stdio.h”中也隻有該函數的聲明,而沒有定義函數的實作,那麼,是在哪裡實作“printf”函數的呢?

最後的答案是:系統把這些函數實作都已經被放入名為libc.so.6的庫檔案中去了,在沒有特别指定時,gcc會到系統預設的搜尋路徑“/usr/lib”下進行查找,也就是連結到libc.so.6庫函數中去,這樣就能實作函數“printf”了,而這也就是連結的作用。

完成了連結之後,gcc就可以生成可執行檔案,其指令如下所示。

[root@localhost gcc]# ./hello

hello, embedded world 5<code>`</code>

本節主要講解gcc的警告提示功能。gcc包含完整的出錯檢查和警告提示功能,它們可以幫助linux程式員寫出更加專業和優美的代碼。

讀者千萬不能小瞧這些警告資訊,在很多情況下,含有警告資訊的代碼往往會有意想不到的運作結果。

首先讀者可以先看一下以下這段代碼:

[root@ft charpter2]# gcc -wall wrong.c -o wrong

wrong.c:4: warning: return type of 'main' is not 'int'

wrong.c: in function 'main':

wrong.c:5: warning: unused variable 'tmp'<code>`</code>

可以看出,使用‘-wall’選項找出了未使用的變量tmp以及傳回值的問題,但沒有找出無效資料類型的錯誤。

2.非wall類警告提示

非wall類的警告提示中最為常用的有以下兩種:“-ansi”和“-pedantic”。

(1)“-ansi”

該選項強制gcc生成标準文法所要求的告警資訊,盡管這還并不能保證所有沒有警告的程式都是符合ansi c标準的。使用該選項的運作結果如下所示:

[root@ft charpter2]# gcc -pedantic wrong.c -o wrong

wrong.c:5: warning: iso c90 does not support 'long long'

wrong.c:4: warning: return type of 'main' is not 'int'<code>`</code>

可以看出,使用該選項檢視出了“long long”這個無效資料類型的錯誤。

1.linux函數庫介紹

函數庫可以看做是事先編寫的函數集合,它可以與主函數分離,進而增加程式開發的複用性。linux中函數庫可以有3種使用的形式:靜态、共享和動态。

靜态庫的代碼在編譯時就已連接配接到開發人員開發的應用程式中,而共享庫隻是在程式開始運作時才載入。

動态庫也是在程式運作時載入,但與共享庫不同的是,動态庫使用的庫函數不是在程式運作使開始載入,而是在程式中的語句需要使用該函數時才載入。動态庫可以在程式運作期間釋放動态庫所占用的記憶體,騰出空間供其他程式使用。

由于共享庫和動态庫并沒有在程式中包括庫函數的内容,隻是包含了對庫函數的引用,是以代碼的規模比較小。

系統中可用的庫都存放在/usr/lib和/lib目錄中。庫檔案名由字首lib和庫名以及字尾組成。根據庫的類型不同,字尾名也不一樣。

stnd001注意 共享庫和動态庫的字尾名由.so和版本号組成。

靜态庫的字尾名為.a。

如:數學共享庫的庫名為libm.so.5,這裡的辨別字元為m,版本号為5,libm.a則是靜态數學庫。在linux系統中系統所用的庫都存放在/usr/lib和/lib目錄中。

2.相關路徑選項

由于庫檔案的通常路徑不是在系統預設的路徑下,是以,首先要使用調用路徑選項來指定相關的庫檔案位置,這裡首先講解兩個常用選項的使用方法。

(1)“-i dir”

在gcc中使用頭檔案在預設情況下是在主程式中所設定的路徑,那麼如果想要改變該路徑,使用者則可以使用“-i”選項。“-i dir”選項可以在頭檔案的搜尋路徑清單中添加dir目錄。這時,gcc就會到相應的位置查找對應的目錄。

比如在“/root/workplace/gcc”下有兩個檔案:

[root@localhost gcc] gcc hello.c –i /root/workplace/gcc/ -o hello<code>`</code>

這樣,gcc就能夠執行出正确結果。

0115小技巧 在include語句中,“&lt;&gt;”表示在标準路徑中搜尋頭檔案,在linux中預設為“/usr/include”。故在上例中,可把hello1.c的“#include”改為“#include "my.h"”,這樣就不需要加上“-i”選項了。

(2)“-l dir”

選項“-l dir”的功能與“-i dir”類似,其差別就在于“-l”選項是用于指明庫檔案的路徑。例如有程式hello_sq.c需要用到目錄“/root/workplace/gcc/lib”下的一個動态庫libsunq.so,則隻需鍵入如下指令即可。

[root@localhost gcc] gcc -o dynamic -l /root/lq/testc/lib/dynamic.o -lmydynamic<code>`</code>

那麼,若系統中同時存在檔案名相同的靜态庫檔案和動态庫檔案時,該連結選項究竟會調用靜态庫檔案還是動态庫檔案呢?

經測試後可以發現,系統調用的是動态庫檔案,這是由于linux系統中預設的是采用動态連結的方式。這樣,若使用者要調用含有同名動态庫檔案的靜态庫檔案,則在“-l”後需要顯示地寫出包含字尾名的檔案名,如:要調用libm.a庫檔案時就需寫作“-llibm.a”。

gcc可以對代碼進行優化,它通過編譯選項-on來控制優化代碼的生成,其中n是一個代表優化級别的整數。對于不同版本的gcc來講,n的取值範圍及其對應的優化效果可能并不完全相同,比較典型的範圍是從0變化到2或3。

不同的優化級别對應不同的優化處理工作,如使用優化選項-o主要進行線程跳轉(thread jump)和延遲退棧(deferred stack pops)兩種優化。

使用優化選項-o2除了完成所有-o1級别的優化之外,同時還要進行一些額外的調整工作,如處理器指令排程等;選項-o3則還包括循環展開和其他一些與處理器特性相關的優化工作。

雖然優化選項可以加速代碼的運作速度,但對于調試而言将是一個很大的挑戰。因為代碼在經過優化之後,原先在源程式中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環語句也有可能因為循環展開而變得到處都有,所有這些都将使調試工作異常堅難。

建議在調試的時候最好不使用任何優化選項,隻有當程式在最終發行的時候才考慮對其進行優化。

繼續閱讀