預處理、動態庫、靜態庫
阿新 • • 發佈:2022-03-24
預處理
c語言編譯過程
Linux下GCC編譯器編譯過程
-
1 預編譯
將.c的標頭檔案展開、巨集定義
生成的檔案是.i檔案 -
2 編譯
將預處理之後的.i檔案生成.s彙編檔案
-
3 彙編
將.s彙編檔案生成.o目標檔案
-
4 連結
將.o檔案連結成目標檔案
gcc -E hello.c -o hello.i 1.預處理
gcc -S hello.i -o hello.s 2.編譯
gcc -c hello.s -o hello.o 3.彙編
gcc hello.o -0 hello_elf 4.連結
include
-
#include <> //用尖括號包含標頭檔案,在系統指定的路徑下找標頭檔案
-
#include "" //用雙引號包含標頭檔案,先在當前目錄下找標頭檔案,找不到,再到系統指定的路徑下找。
-
注意: include 經常用來包含標頭檔案,可以包含 .c 檔案,但是不要包含 .c ,因為include包含的檔案會在預編譯被展開,如果一個.c被包含多次,展開多次,會導致函式重複定義。所以不要包含.c檔案。
-
注意:預處理只是對include等預處理操作進行處理並不會進行語法檢查,這個階段有語法錯誤也不會報錯,第二個階段即編譯階段才進行語法檢查。
define
-
巨集定義用define定義
-
巨集是在預編譯的時候進行替換
不帶參巨集
作用範圍: 從定義的地方到本檔案末尾,如果想在中間終止巨集的定義範圍#undef 變數名
//測試巨集定義
#define PI 3.14
int main(){
printf("PI = %f",PI); //PI = 3.140000
#undef PI //撤銷巨集定義
//printf("PI = %f",PI); // 編譯都通不過,因為巨集定義已經撤銷了
#define PI 3.1415926 //重新巨集定義
printf("PI = %f",PI); //PI = 3.141593
return 0;
};
帶參巨集#define S(a,b) a*b
-
注意帶參巨集的形參a和b沒有型別名
-
S(2,4)將來在預處理的時候替換成 實參替代字串的形參,其他字元保留
//代參巨集定義
#define S(a,b) a*b
#define B(a,b) (a)*(b) //保證優先順序,消歧義
int main(){
int a = S(2,3);
printf("a = %d\n",a); // a = 6
a= S(3+2,3);
printf("a = %d\n",a); // a = 9 ,相當於 3+2*3 沒有括號可能有歧義
a= B(3+2,3);
printf("a = %d\n",a); //a = 15
return 0;
}
帶參巨集與帶參函式區別
- 帶參巨集被呼叫多少次就會被展開多少次,執行程式碼的時候沒有函式呼叫的過程,不需要壓棧彈棧。所以帶參巨集,是浪費了空間,因為被展開多次,節省時間
- 帶參函式,程式碼只有一行,存在程式碼段,呼叫的時候去程式碼段取程式碼,呼叫的時候要。壓棧彈棧。有個呼叫的過程。
- 所以說,帶參函式浪費了時間,節省了空間
- 帶參函式的形參是有型別的,帶參巨集的形參沒有型別名
選擇性編譯
ifdef
//選擇性編譯
#ifdef S // S代表一個巨集
//如果巨集存在,就會編譯
printf("巨集存在,編譯");
#else
//不存在,就不會編譯
printf("巨集不存在,不編譯");
#endif
ifndef
#ifndef S
//如果巨集存在,就會編譯
printf("巨集存在,不編譯");
#else
//不存在,就不會編譯
printf("巨集不存在,編譯");
#endif
這種方法常用語放置標頭檔案重複包含
#ifndef _5h //沒有定義才會編譯
#define _5h //下面定義了,所以下次引入就不會在編譯了
extern int a;
#endif
if
#if 1
//如果表示式為真,就會編譯
printf("表示式為真,編譯");
#else
//如果表示式為假,就不會編譯
printf("表示式為假,不編譯");
#endif
靜態庫
1.動態編譯
- 動態編譯使用的是動態庫檔案進行編譯
gcc hello.c -o hello
- 預設使用動態編譯方法
2.靜態編譯
- 靜態編譯使用的靜態庫檔案進行編譯
gcc -static hello.c -o hello
3.靜態編譯和動態編譯區別
- 1 使用庫檔案格式不一樣,動態編譯使用動態庫,靜態編譯使用靜態庫
- 2 態編譯要把靜態庫檔案打包編譯到可執行程式中
- 3 動態編譯不會把動態庫檔案打包編譯到可執行程式中,他只是編譯連結關係