在Linux下如何使用GCC編譯程序、簡單生成 靜態庫及動態庫。
本文適用於Linux下開發初學者。本文初步講解在Linux下如何使用GCC編譯程序、簡單生成靜態庫及動態庫。
1、關於安裝。通常系統默認是安裝好編譯器的,而且網絡上有大量資料介紹不一樣發行版本下的安裝問題,本文再也不描述。
2、C編程中的文件後綴名介紹
.a 靜態庫(打包文件)
.c 未通過預處理的C源碼
.h C頭文件
.i 通過預處理的C源碼
.o 編譯以後產生的目標文件
.s 生成的彙編語言代碼
.so 動態庫(動態連接庫)
解釋:*.a是咱們在編譯事後用ar打包生成的靜態庫;*.c通常使咱們本身編輯的代碼,使咱們勞動的結晶;*.h通常是 咱們手工生成的接口文件,若是願意,也可在*.c完成後用GCC的選項-aux-info幫咱們生成;*.i是通過預處理後的源碼,是由GCC在選項-E編譯下自動生成 的文件;*.o是編 譯後產生的目標文件;*.s是GCC在選項-S編譯下生成的彙編語言代碼,對於性能要求很高的程序能夠先生成彙編語言文件並對彙編作優化,而後用優 化後的彙編生成目標文件並連接;*.so是動態庫,經過GCC的-fpic -shared選項生成。
3、hello.c的編譯過程
本小節的演示都針對文件 hello.c 進行
- /*
- * hello.c
- */
-
- #include <stdio.h>
- int main()
- {
- printf("hello, world!/n");
- return 0;
- }
1. 直接生成可執行程序
- $ gcc -o hello hello.c
- $ ./hello
- hello, world!
-
- 如 下編譯方式 結果相同:
- $ gcc hello.c -o hello
- $ ./hello
- hello, world!
-
- 如 下編譯方式 有別於以上編譯方 案(具體查找ELF和a.out文件格式差異的網絡資料,對於此處結果是無任何區別的):
- $ gcc hello.c
- $ ./a.out
- hello, world!
2. 生成預處理後的文件 hello.i
- $ gcc -E hello.c -o hello.i
- $ ls
- a.out hello hello.c hello.i
- hello.i 就 是新生成的文件
-
- 以下語句結果相同:
- $ gcc -E -o hello.i hello.c
-
- 如 果不設定輸出文件,則打印到標準終端,此時咱們能夠用 less 查看:
- $ gcc -E hello.c | less
- # 1 "hello.c"
- # 1 "<built-in>"
- # 1 "<command line>"
- # 1 "hello.c"
- # 1 "/usr/include/stdio.h" 1 3 4
- # 28 "/usr/include/stdio.h" 3 4
- # 1 "/usr/include/features.h" 1 3 4
- # 329 "/usr/include/features.h" 3 4
- ..............................
-
- 或 者執行:
- $ gcc -E hello.c -o hello.i
- $ vi hello.i
- 1 # 1 "hello.c"
- 2 # 1 "<built-in>"
- 3 # 1 "<command line>"
- 4 # 1 "hello.c"
- 5 # 1 "/usr/include/stdio.h" 1 3 4
- 6 # 28 "/usr/include/stdio.h" 3 4
- 7 # 1 "/usr/include/features.h" 1 3 4
- 8 # 329 "/usr/include/features.h" 3 4
-
- .......... < 中間部分略> ..................
-
- 929 # 844 "/usr/include/stdio.h" 3 4
- 930
- 931 # 2 "hello.c" 2
- 932
- 933 int main()
- 934 {
- 935 printf("hello, world!/n");
- 936
- 937 return 0;
- 938 }
-
- 可 見,將近1000行的代碼,咱們的只佔了最末8行。
-
3.生成彙編語言文件 hello.s
- $ gcc -S hello.c -o hello.s
- $ ls
- a.out hello hello.c hello.i hello.s
- hello.s 就是新生成的文件
-
- 以下語句結果相同:
- $ gcc -S -o hello.s hello.c
-
- 如 下語句結果相同:
- $ gcc -S hello.c
-
- 也 能夠採用前一步驟產生的中間文件生成彙編文件:
- $ gcc -S hello.i -o hello.s
- $ gcc -S -o hello.s hello.i
- $ gcc -S hello.i
-
-
- 生 成的彙編部分代碼以下:
- $ vi hello.s
- 1 .file "hello.c"
- 2 .section .rodata
- 3 .LC0:
- 4 .string "hello, world!"
- 5 .text
- 6 .globl main
- 7 .type main, @function
- 8 main:
- 9 leal 4(%esp), %ecx
- 10 andl $-16, %esp
- 11 pushl -4(%ecx)
- 12 pushl %ebp
- // 註釋:若是你熟悉,就能夠對部分彙編優化以達到更好效果。
4.生成目標文件 hello.o
- $ gcc -c hello.c -o hello.o
- $ ls
- a.out hello hello.c hello.i hello.o hello.s
- hello.o 就是新生成的目標文件:
-
- 以下語句結果相同:
- $ gcc -c -o hello.o hello.c
-
- 如 下語句結果相同:
- $ gcc -c hello.c
-
- 也 能夠採用前面步驟產生的中間文件hello.i或hello.s來生成目標文件:
- $ gcc -c hello.i
- $ gcc -c hello.s
-
- 我 們能夠用 objdump 查看 hello.o 的二進制碼:
- $ objdump -s hello.o
-
- hello.o: file format elf32-i386
-
- Contents of section .text:
- 0000 8d4c2404 83e4f0ff 71fc5589 e55183ec .L$.....q.U..Q..
- 0010 04c70424 00000000 e8fcffff ffb80000 ...$............
- 0020 000083c4 04595d8d 61fcc3 .....Y].a..
- Contents of section .rodata:
- 0000 68656c6c 6f2c2077 6f726c64 2100 hello, world!.
- Contents of section .comment:
- 0000 00474343 3a202847 4e552920 342e312e .GCC: (GNU) 4.1.
- 0010 31203230 30373031 30352028 52656420 1 20070105 (Red
- 0020 48617420 342e312e 312d3532 2900 Hat 4.1.1-52).
5. 採用中間級文件生成可執行程序
- $ gcc -o hello hello.i
- $ ./hello
- hello, world!
-
- $ gcc -o hello hello.s
- $ ./hello
- hello, world!
-
- $ gcc -o hello hello.o
- $ ./hello
- hello, world!
4、 靜態庫的生成
linux下靜態庫的生成比較方便。在生成目標文件後用 ar 打包便可。在中大型項目中一個模塊通常會作成一個靜態庫,以方便管理、提升編譯、連接效率。
本小節的展現針對 main.c、func1.c、func2.c三個文件
- /*
- * main.c
- */
- #include <stdio.h>
-
- extern int func1();
- extern int func2();
-
- int main()
- {
- int i;
-
- i = func1();
- printf("func1 return = %d/n",i);
-
- i = func2();
- printf("func2 return = %d/n",i);
-
- return 0;
- }
-
-----------------------------------------------------
- /*
- * func1.c
- */
- int func1()
- {
- return 100;
- }
-----------------------------------------------------
- /*
- * func2.c
- */
- int func2()
- {
- return 200;
- }
一 下是編譯指 令:
- $ gcc -c func1.c
- $ gcc -c func2.c
- $ ls
- func1.c func1.o func2.c func2.o main.c
-
- func1.o 和 func2.o 是 咱們生成的目標文件。打包指令以下:
- $ ar -r libfunc.a func1.o func2.o
-
- 我 們查看 libfunc.a 中的文件:
- $ ar -t libfunc.a
- func1.o
- func2.o
-
- 現 在用靜態庫和 main.c 共同生成目標程序:
- $ gcc -o main main.c libfunc.a
- $ ./main
- func1 return = 100
- func2 return = 200
-
- 和 咱們的預期相符合。下面咱們進入動態庫。
-
5、動態庫的生成
linux下動態庫的生成經過GCC選項實現。案例程序和靜態庫中的相同。一下是操做指令:
- 首 先咱們生成目標文件,可是須要加編譯器選項 -fpic 和連接器選項 -shared
- $ gcc -fpic -c func1.c
- $ gcc -fpic -c func2.c
- $ gcc -shared -o libfunc.so func1.o func2.o
- $ ls
- func1.c func1.o func2.c func2.o libfunc.so main.c
-
- libfunc.so 就是咱們生成的目標動態庫。咱們用動態庫和 main.c 生成目標程序:
- $ gcc -o main main.c -L. -lfunc
-
- 注 意,咱們用 -L. -lfunc 做爲編譯選項。-L. 表從當前目錄查找須要的動態庫,-lfunc 是動態庫的調用規則。Linux系統下的動態庫命名方 式是 lib*.so,而在連接時表示位 -l* , *是本身起的庫名。下面咱們運行它:
-
- $ ./main
- ./main: error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory
-
- 提 示一個錯誤, 指示沒法找到動態庫。在linux下最方便的解決方案是拷貝libfunc.so到絕對目錄 /lib 下。可是隻有超級用戶纔有這個權限。另一個方案 是更改環境變量 LD_LIBRARY_PATH。以下:
- $ $ export LD_LIBRARY_PATH=`pwd`
- $ ./main
- func1 return = 100
- func2 return = 200
-
- 運 行成功。如今咱們更改動態庫的函數而不從新連接。以下:
- 更改 func1.c 爲:
- int func1()
- {
- return 101;
- }
- 更 改 func2.c 爲:
- int func2()
- {
- return 202;
- }
- 重 新生成庫:
- $ gcc -fpic -shared func1.c func2.c -o libfunc.so
- $ ./main
- func1 return = 101
- func2 return = 202
-
- 可 以看出,動態庫已經更新了。
6、結束語 本文簡單介紹了linux下如何使用gcc進行編譯程序、以及簡 單的靜態、動態庫的生成。靜態庫提供了一種打包管理方案,而動態庫使程序局部更新成爲了可能,更重要的是,當有多份實例存在時,動態庫可減少內存的消耗 (只佔用一份代碼空間)。 對本系列知識感興趣者可繼續跟蹤閱讀後續文章:庫的版本管理、GCC的編譯選項、Makefile與自動化編譯