GCC 指令詳解及動態庫、靜態庫的使用( 二 )


答案就是:系統把這些函數實現都做到了名為 libc.so.6 的庫文件中去了 , 在沒有特別指定時 , gcc 會到系統默認的搜索路徑 /usr/lib64 下進行查找 , 也就是鏈接到 libc.so.6 庫函數中去 , 這樣就有函數 printf 的實現了,而這也就是鏈接的作用 。

GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
而函數庫一般分為靜態庫和動態庫兩種:
  • 靜態庫是指在編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不需要庫文件了 。在 Linux 中靜態庫一般以 .a 作為后綴 。
  • 動態庫與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時鏈接文件加載庫,這樣就可以節省系統的開銷 。在 Linux 中動態庫一般以 .so 作為后綴 。
如前面所述的 libc.so.6 就是動態庫 , gcc 在編譯時默認使用動態庫 。完成了鏈接之后,gcc 就可以生成可執行文件了 。
有關動態庫和靜態庫的詳細介紹,將在下文進行具體講解 。
1.3.2.5 總結最后 , 通過一張圖來總結一下上述流程:
GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
在 Linux 下使用 GCC 編譯器編譯單個文件十分簡單,直接使用$ gcc test.c(test.c 為要編譯的 C 語言的源文件) , GCC 會自動生成文件名為 a.out 的可執行文件(也可以通過參數 -o 指定生成的文件名);也就是通過一個簡單的命令就可以將上邊提到的 4 個步驟全部執行完畢了;但是如果想要單步執行也是沒問題的 。
1.4 GCC 常用參數下面的表格中列出了一些常用的 gcc 參數,這些參數在 gcc 命令中沒有位置要求,只需要編譯程序的時候將需要的參數指定出來即可 。
gcc 編譯選項解釋說明-E預處理,主要是進行宏展開等步驟,生成 test.i-S編譯指定的源文件,但是不進行匯編,生成 test.s-c編譯、匯編源文件 , 但是不進行鏈接,生成 test.o-o指定鏈接的文件名及路徑-g在編譯的時候,生成調試信息,該程序可以被調試器調試-D在程序編譯的時候,指定一個宏-std指定 C 方言,如 -std=c99 。gcc 默認的方言是 GNU C-l在程序編譯的時候,指定使用的庫(庫的名字一定要掐頭去尾,如 libtest.so 變為 test)-L在程序編譯的時候 , 指定使用的庫的路徑-fpic生成與位置無關的代碼-shared生成共享目標文件,通常用在建立動態庫時1.4.1 指定一個宏(-D)在程序中我們可以通過使用#define定義一個宏,也可以通過宏控制某段代碼是否能夠被執行 。
#include <stdio.h>int main(){int num = 60;printf("num = %d\n", num);#ifdef DEBUGprintf("定義了 DEBUG 宏, num++\n");num++;#elseprintf("未定義 DEBUG 宏, num--\n");num--;#endifprintf("num = %d\n", num);return 0;}由于我們在程序中并沒有定義 DEBUG 宏,所以第 8~9 行的代碼就不會被執行:
GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
那么如何才能夠在程序中不定義 DEBUG 宏的情況下執行第 8~9 行的代碼呢?答案是通過 -D 參數:
GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
需要注意的是,-D 參數必須在生成 test.o 前使用(鏈接前) 。如下所示,是無效的:
GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
說了這么多,-D 參數有什么用呢?下面我們簡單敘述一下 -D 參數的應用場景 。
1.4.1.1 應用場景一在發布程序的時候,一般都會要求將程序中所有的 log 輸出去掉,如果不去掉會影響程序的執行效率 , 很顯然刪除這些打印 log 的源代碼是一件很麻煩的事情 , 解決方案是這樣的:
  1. 將所有的打印 log 的代碼都寫到一個宏判定中,可以模仿上邊的例子;
  2. 在調試程序的時候指定 -D,就會有 log 輸出;
  3. 在發布程序的時候不指定 -D , log 就不會輸出;
1.4.1.2 應用場景二或者,你編寫的一個軟件,某個付費功能只對已付費的用戶 A 開放,但不對白嫖的用戶 B 開放,其中一種解決方法是:
  1. 每個用戶對應一個維護分支 , 用戶 A 對應 project_1 分支包含付費功能的代碼,用戶 B 對應的 project_2 分支不包含付費功能的代碼 。
  2. 當用戶 B 付費訂閱時,再將付費項目的代碼拷貝到 project_2 中

    推薦閱讀