1. 程式人生 > >GCC常用命令詳解

GCC常用命令詳解

GCC(GNU Compiler Collection)是Linux下最常用的C語言編譯器,是GNU專案中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object C等語言編寫的程式。同時它可以通過不同的前端模組來支援各種語言,如Java、Fortran、Pascal、Modula-3和Ada等。
穿插一個玩笑: GNU意思是GNU’s not Unix而非角馬。然而GNU還是一個未拆分的連詞,這其實是一個源於hacker的幽默:GNU是一個迴文遊戲,第一個字母G是湊數的,你當然可以叫他做ANU或者BNU。
下面開始。

一.CC編譯程式過程分四個階段
◆ 預處理(Pre-Processing)

◆ 編譯(Compiling)
◆ 彙編(Assembling)
◆ 連結(Linking)

Linux程式設計師可以根據自己的需要讓GCC在編譯的任何階段結束轉去檢查或使用編譯器在該階段的輸出資訊,或者對最後生成的二進位制檔案進行控制,以便通過加入不同數量和種類的除錯程式碼來為今後的除錯做好準備。如同其他的編譯器,GCC也提供了靈活而強大的程式碼優化功能,利用它可以生成執行效率更高的程式碼。
GCC提供了30多條警告資訊和三個警告級別,使用它們有助於增強程式的穩定性和可移植性。此外,GCC還對標準的C和C++語言進行了大量的擴充套件,提高程式的執行效率,有助於編譯器進行程式碼優化,能夠減輕程式設計的工作量。



二.簡單編譯命令
我們以Hello world程式來開始我們的學習。程式碼如下:

/* hello.c */
#include <stdio.h>
int main(void)
{
printf ("Hello world!\n");
return 0;
}

1. 執行如下命令:$ gcc -o hello hello.c
執行如下 : $ ./hello
輸出: Hello,world!
2. 我們也可以分步編譯如下:
(1) $ gcc –E hello.c -o hello.i 
//預處理結束
//這時候你看一下hello.i ,可以看到插進去了很多東西。
(2) $ gcc –S hello.i
//生成彙編程式碼後結束
(3) $ gcc –c hello.c

或者:
$ gcc -c hello.c –o hello.o
或者:
$ gcc -c hello.i -o hello.o
//編譯結束
//生成 hello.o檔案
(4) $ gcc hello.o –o hello.o
或者:
$ gcc –o hello hello.c
//連結完畢,生成可執行程式碼

3. 我們可以把幾個檔案一同編譯生成同一個可執行檔案。
比如:一個工程有main.c foo.c def.c生成foo的可執行檔案。
編譯命令如下:
$ gcc –c main.c foo.c def.c –o foo
或者:
$ gcc –o foo main.c foo.c def.c
三.庫依賴
函式庫是一些標頭檔案(.h)和庫檔案(.so或者.a)的集合。Linux下的大多數函式都預設將標頭檔案放到/usr/include/目錄下,而庫檔案則放到/usr/lib/目錄下,但並非絕對如此。因此GCC設有新增標頭檔案和庫檔案的編譯選項開關。
1. 新增標頭檔案:-I
例如在/home/work/include/目錄下有編譯foo.c所需標頭檔案def.h,為了讓GCC能找到它們,就需要使用-I選項:
$ gcc foo.c -I /home/work/include/def.h -o foo
2. 新增庫檔案:-L
例如在/home/work/lib/目錄下有連結所需庫檔案libdef.so,為了讓GCC能找到它們,就需要使用-L選項:
$ gcc foo.c –L /home/work/lib –ldef.a –o foo
說明:-l選項指示GCC去連線庫檔案libdef.so。Linux下的庫檔案命名有一個約定,即庫檔案以lib三個字母開頭,因為所有的庫檔案都遵循這個約定,故在用-l選項指定連結的庫檔名時可以省去lib三個字母。
[題外語] 
Linux下的庫檔案分為動態連結庫(.so檔案)和靜態連結庫(.a檔案)。GCC預設為動態庫優先,若想在動態庫和靜態庫同時存在的時候連結靜態庫需要指明為-static選項。比如上例中如還有一個libdef.a而你想連結libdef.a時候命令如下:
$ gcc foo.c –L /home/work/lib –static –ldef.a –o foo
四.程式碼優化
GCC提供不同程度的程式碼優化功能。開關選項是:-On,n取值為0到3。預設為1。-O0表示沒有優化,而-O3是最高優化。優化級別越高程式碼執行越快,但並不是所有程式碼都能夠載入最高優化,而應該視具體情況而定。但一般都使用-O2選項,因為它在優化長度、編譯時間和程式碼大小之間,取得了一個比較理想的平衡點。
以下這段是我摘自別人文章的,說的比較詳細:
編譯時使用選項-O可以告訴GCC同時減小程式碼的長度和執行時間,其效果等價於-O1。在這一級別上能夠進行的優化型別雖然取決於目標處理器,但一般都會包括執行緒跳轉(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優化。選項-O2告訴GCC除了完成所有-O1級別的優化之外,同時還要進行一些額外的調整工作,如處理器指令排程等。選項-O3則除了完成所有-O2級別的優化之外,還包括迴圈展開和其它一些與處理器特性相關的優化工作。通常來說,數字越大優化的等級越高,同時也就意味著程式的執行速度越快。
下面通過具體例項來感受一下GCC的程式碼優化功能,所用程式如清單3所示。
/* optimize.c */
#include <stdio.h>
int main(void)
{
double counter;
double result;
double temp;
for (counter = 0; 
counter < 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; 
counter += (5 - 1) / 4) {
temp = counter / 1979;
result = counter; 
}
printf("Result is %lf\n", result);
return 0;
}

首先不加任何優化選項進行編譯:
# gcc -Wall optimize.c -o optimize
藉助Linux提供的time命令,可以大致統計出該程式在執行時所需要的時間:
# time ./optimize
Result is 400002019.000000
real 0m14.942s
user 0m14.940s
sys 0m0.000s
接下去使用優化選項來對程式碼進行優化處理:
# gcc -Wall -O optimize.c -o optimize
在同樣的條件下再次測試一下執行時間:
# time ./optimize
Result is 400002019.000000
real 0m3.256s
user 0m3.240s
sys 0m0.000s
對比兩次執行的輸出結果不難看出,程式的效能的確得到了很大幅度的改善,由原來的14秒縮短到了3秒。這個例子是專門針對GCC的優化功能而設計的,因此優化前後程式的執行速度發生了很大的改變。儘管GCC的程式碼優化功能非常強大,但作為一名優秀的Linux程式設計師,首先還是要力求能夠手工編寫出高質量的程式碼。如果編寫的程式碼簡短,並且邏輯性強,編譯器就不會做更多的工作,甚至根本用不著優化。
優化雖然能夠給程式帶來更好的執行效能,但在如下一些場合中應該避免優化程式碼:
◆ 程式開發的時候優化等級越高,消耗在編譯上的時間就越長,因此在開發的時候最好不要使用優化選項,只有到軟體發行或開發結束的時候,才考慮對最終生成的程式碼進行優化。
◆ 資源受限的時候一些優化選項會增加可執行程式碼的體積,如果程式在執行時能夠申請到的記憶體資源非常緊張(如
一些實時嵌入式裝置),那就不要對程式碼進行優化,因為由這帶來的負面影響可能會產生非常嚴重的後果。
◆ 跟蹤除錯的時候在對程式碼進行優化的時候,某些程式碼可能會被刪除或改寫,或者為了取得更佳的效能而進行重組,從而使跟蹤和除錯變得異常困難。