C 編程中相關檔案尾碼
| .a |
靜態庫 (archive) |
| .c |
C原始碼(需要編譯預先處理) |
| .h |
C原始碼標頭檔 |
| .i |
C原始碼(不需編譯預先處理) |
| .o |
對象檔案 |
| .s |
組合語言代碼 |
| .so |
動態庫 |
[編輯]
單個源檔案產生可執行程式
下面是一個簡單的“hello, ubuntu”程式的原始碼:
/* helloubuntu.c */#include <stdio.h>int main(int argc,char *argv[]){printf("hello, ubuntu\n"); return 0;}
最簡單直接的編譯該代碼為可執行程式的方法是,將該代碼儲存為檔案 helloubuntu.c,並執行以下命令:
$ gcc -Wall helloubuntu.c
編譯器通過檢查命令列中指定的檔案的尾碼名可識別其為 C 原始碼檔案。GCC 預設的動作:編譯原始碼檔案產生對象檔案(object file),連結化物件檔案得到可執行程式,刪除對象檔案。由於命令列中未指定可執行程式的檔案名稱,編譯器採用預設的 a.out。在命令列中輸入程式名可使其執行並顯示結果:
$ ./a.outhello, ubuntu
選項 -o 用來指定所產生的可執行程式的檔案名稱。下面的命令產生名為 helloubuntu 的可執行程式:
$ gcc -Wall helloubuntu.c -o helloubuntu
在命令列中輸入程式名將使其執行,如下:
$ ./helloubuntuhello, ubuntu
注意如果有用到math.h庫等非gcc預設調用的標準庫,請使用-lm參數
[編輯]
源檔案產生對象檔案
選項 -c 指示 GCC 編譯原始碼檔案,但將對象檔案保留在磁碟中並跳過連結化物件檔案產生可執行檔這一步。在這種情況下,預設的輸出檔案的檔案名稱同原始碼檔案名稱一致,只不過尾碼換為 .o 。例如:下面的命令將產生名為 helloubuntu.o 的對象檔案:
$ gcc -c -Wall helloubuntu.c
選項 -o 可用來指定產生的對象檔案的檔案名稱。以下命令將產生名為kubuntu.o的對象檔案:
$ gcc -c -Wall helloubuntu.c -o kubuntu.o
當構建物件程式庫或者產生一系列對象檔案以備稍後連結用時,一條命令即可從多個源碼檔案產生對應的對象檔案。下面的命令將產生對象檔案ubuntu.o, kubuntu.o 與 xubuntu.o:
$ gcc -c -Wall ubuntu.c kubuntu.c xubuntu.c
[編輯]
多個源檔案產生可執行程式
即使多個源碼檔案被編譯,GCC編譯器也會自動進行連結操作。例如:下面的代碼儲存在名為 hellomain.c 的檔案中並調用一個名為 sayhello()的函數:
/* hellomain.c */void sayhello(void);int main(int argc,char *argv[]){sayhello();return 0;}
以下代碼儲存在名為 sayhello.c 的檔案中並定義了 sayhello() 函數:
/* sayhello.c */#include <stdio.h>void sayhello(){printf("hello, ubuntu\n");/*這裡有個小錯誤,是中文IME造成的引號使gcc報錯*/}
下面的命令將兩個檔案分別編譯為對象檔案且將其連結為可執行程式 hello,並刪除對象檔案:
$ gcc -Wall hellomain.c sayhello.c -o hello
[編輯]
編譯預先處理
選項 -E 指示編譯器只進行編譯預先處理。下面的命令將預先處理源碼檔案 helloubuntu.c 並將結果在標準輸出中列出:
$ gcc -E helloubuntu.c
選項 -o 用來將預先處理過的代碼定向到一個檔案。像本文一開始給出的尾碼列表所給出的,不需經過預先處理的C源碼檔案儲存為尾碼為 .i的檔案中,這種檔案可以這樣來獲得:
$ gcc -E helloubuntu.c -o helloubuntu.i
[編輯]
產生彙編代碼
選項 -S 指示編譯器產生組合語言代碼然後結束。下面的命令將由 C 源碼檔案 helloubuntu.c 產生組合語言檔案 helloubuntu.s:
$ gcc -S helloubuntu.c
組合語言的形式依賴於編譯器的目標平台。如果多個源碼檔案被編譯,每個檔案將分別產生對應的彙編代碼模組。
[編輯]
建立靜態庫
靜態庫是編譯器產生的普通的 .o 檔案的集合。連結一個程式時用庫中的對象檔案還是目錄中的對象檔案都是一樣的。靜態庫的另一個名字叫歸檔檔案(archive),管理這種歸檔檔案的工具叫 ar 。
要構建一個庫,首先要編譯出庫中需要的對象模組。例如,下面的兩個源碼檔案為 hellofirst.c 和 hellosecond.c:
/* hellofirst.c */#include <stdio.h>void hellofirst(){printf(“The first hello\n”);}
/* hellosecond.c */#include <stdio.h>void hellosecond(){printf(“The second hello\n”);}
這兩個源碼檔案可以用以下命令編譯成對象檔案:
$ gcc -c -Wall hellofirst.c hellosecond.c
程式 ar 配合參數 -r 可以建立一個新庫並將對象檔案插入。如果庫不存在的話,參數 -r 將建立一個新的,並將對象模組添加(如有必要,通過替換)到歸檔檔案中。下面的命令將建立一個包含本例中兩個對象模組的名為 libhello.a 的靜態庫:
$ ar -r libhello.a hellofirst.o hellosecond.o
現在庫已經構建完成可以使用了。下面的程式 twohellos.c 將調用該庫中的這兩個函數:
/* twohellos.c */void hellofirst(void);void hellosecond(void);int main(int argc,char *argv[]){hellofirst();hellosecond();return 0;}
程式 twohellos 可以通過在命令列中指定庫用一條命令來編譯和連結,命令如下:
$ gcc -Wall twohellos.c libhello.a -o twohellos
靜態庫的命名慣例是名字以三個字母 lib 開頭並以尾碼 .a 結束。所有的系統庫都採用這種命名慣例,並且它允許通過 -l(ell) 選項來簡寫命令列中的庫名。下面的命令與先前命令的區別僅在於 gcc 期望的找尋該庫的位置不同:
$ gcc -Wall twohellos.c -lhello -o twohellos
指定完整的路徑名可使編譯器在給定的目錄中尋找庫。庫名可以指定為絕對路徑(比如 /usr/worklibs/libhello.a)或者相對與目前的目錄的路徑(比如 ./lib/libhello.a)。選項 -l 不能具有指定路徑的能力,但是它要求編譯器在系統庫目錄下找尋該庫。
[編輯]
建立共用庫
共用庫是編譯器以一種特殊的方式產生的對象檔案的集合。對象檔案模組中所有地址(變數引用或函數調用)都是相對而不是絕對的,這使得共用模組可以在程式的運行過程中被動態地調用和執行。
要構建一個共用庫,首先要編譯出庫中需要的對象模組。例如:下面是檔案名稱為 shellofirst.c 和 shellosecond.c 的兩個源碼檔案:
/* shellofirst.c */#include <stdio.h>void shellofirst(){printf(“The first hello from a shared library\n”);}/* shellosecond.c */#include <stdio.h>void shellosecond(){printf(“The second hello from a shared library\n”);}
要將以上兩個源碼檔案編譯成對象檔案,可以用下面的命令:
$ gcc -c -Wall -fpic shellofirst.c shellosecond.c
選項 -c 告訴編譯器只產生 .o 的對象檔案。選項 -fpic 使產生的對象模組採用浮動的(可重定位的)地址。縮微詞 pic 代表“位置無關代碼”(position independent code)。
下面的 gcc 命令將對象檔案構建成一個名為 hello.so 的共用庫:
$ gcc -Wall -shared shellofirst.o shellosecond.o -o hello.so
選項 -o 用來為輸出檔案命名,而檔案尾碼名 .so 告訴編譯器將對象檔案連結成一個共用庫。通常情況下,連結器定位並使用 main() 函數作為程式的入口,但是本例中輸出模組中沒有這種進入點,為抑制錯誤選項 -shared 是必須的。
編譯器能將尾碼為 .c 的檔案識別為 C 語言原始碼檔案,並知道如何將其編譯成為對象檔案。基於這一點,先前的兩條命令我們可以合并為一條;下面的命令直接將模組編譯並儲存為共用庫:
$ gcc -Wall -fpic -shared shellofirst.c shellosecond.c -o hello.so
下面的程式,儲存在檔案 stwohellos.c 內,是調用共用庫中兩個函數的主程式:
/* stwohellos.c */void shellofirst(void);void shellosecond(void);int main(int argc,char *argv[]){shellofirst();shellosecond();return 0;}
該程式可以用下面的命令編譯並連結共用庫:
$ gcc -Wall stwohellos.c hello.so -o stwohellos
程式 stwohello 已經完成,但要運行它必須讓其能定位到共用庫 hello.so,因為庫中的函數要在程式運行時被載入。 需要注意的是,當前工作目錄可能不在共用庫的尋找路徑中,因此需要使用如下的命令列設定環境變數LD_LIBRARY_PATH:
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
[編輯]
超越命名慣例
如果環境要求你使用 .c 以外的尾碼名來命名你的 C 源碼檔案,你可以通過 -x 選項來指定其對應的語言以忽略我們的命名規範。例如,下面的命令將從檔案 helloworrld.jxj 編譯 C 語言原始碼並產生可執行檔 helloubuntu:
$ gcc -xc helloubuntu.jxj -o helloubuntu
通常,在沒有 -x 選項的情況下,任何具有未知尾碼名的源碼檔案名稱都被認為是連接器可以識別的選項,並在不做任何更改的情況下傳遞給連結器。選項 -x 對其後的所有未知尾碼的檔案都起作用。例如,下面的命令使 gcc 將 align.zzz 和 types.xxx 都作為 C 源碼檔案來處理:
$ gcc -c -xc align.zzz types.xxx