include預處理機制詳解

2021-07-11 13:35:54 字數 3776 閱讀 4302

1.  #include 命令的作用

1.1  什麼情況不使用 include

檔案 

void test_a()   

檔案 void test_a();  // 函式宣告 

void test_b() 

其實,這樣的工程,可以不用使用 include 預編譯命令。

1.2  什麼情況使用 include

如果工程裡面的函式特別多,那麼按照上面的做法,則必須在每乙個 .c 檔案的開頭列出所有本檔案呼叫過的函式的宣告,這樣很不高效,而且一旦某個函式的形式發生變化,又得乙個乙個改 .c 開頭的函式宣告。 

因此,#include 預編譯命令誕生。

檔案 

void test_a()   

檔案 void test_a(); 

檔案 #include "a.h"    // 包含含有 test_a() 函式宣告的標頭檔案 

void test_b() 

1.3  #include 起到什麼效果

上述**在編譯器進行預編譯的時候,

遇到 #include "a.h" ,則會把整個 a.h 檔案都copy到 b.c 的開頭,

因此,在實際編譯 b.c 之前,b.c 已經被修改為了如下形式:

預編譯後的臨時檔案 

void test_a(); 

void test_b() 

由此可見,得到的效果和手動加 test_a() 函式宣告時的效果相同。

#tips# 在linux下,可以使用 gcc -e b.c 來檢視預編譯 b.c 後的效果。

2.1  什麼叫函式重複定義

我們經常會遇到報錯,說變數或者函式重複定義。那麼,在此,首先我舉例說明一下什麼叫函式的重複定義。

檔案 

void test()   

檔案 void test() 

那麼,在編譯的時候是不會報錯的,但是,在鏈結的時候,會出現報錯:

multiple definition of `test',因為在同乙個工程裡面出現了兩個test函式的定義。

2.2  在.h裡面寫函式實現

如果在 .h 裡面寫了函式實現,會出現什麼情況?

檔案 

void test_a()   

檔案 #include "a.h" 

void test_b() 

預編譯後,會發現,b.c 被修改為如下形式:

預編譯後的臨時檔案 

void test_a()   

void test_b() 

當然,這樣目前是沒有什麼問題的,可以正常編譯鏈結成功。但是,如果有乙個 c.c 也包含的 a.h 的話,怎麼辦?

檔案 

#include "a.h" 

void test_c() 

同上,c.c 在預編譯後,也形成了如下**:

// c.c 預編譯後的臨時檔案 

void test_a()   

void test_c() 

那麼,在鏈結器進行鏈結(link)的時候,會報錯:

multiple definition of `test_a'

因此,在 .h 裡面寫函式實現的弊端就暴露出來了。但是,經常會有這樣的需求,將乙個函式設定為 內聯(inline) 函式,並且放在 .h 檔案裡面,那麼,怎樣才能防止出現上述 重複定義的報錯呢?

用static修飾函式,則表明該函式只能在本檔案中使用,因此,當不同的檔案中有相同的函式名被static修飾時,不會產生重複定義的報錯。例如:

檔案 

static void test()   

void test_a()   

檔案 static void test()   

void test_b() 

編譯工程時不會報錯,但是test()函式只能被 a.c 和 b.c 中的函式呼叫,不能被 c.c 等其他檔案中的函式呼叫。

那麼,用static修飾 .h 檔案中定義的函式,會有什麼效果呢?

檔案 

static void test()   

檔案 #include "a.h" 

void test_b()   

檔案 #include "a.h" 

void test_c() 

3.  防止標頭檔案重複包含

經常寫程式的人都知道,我們在寫 .h 檔案的時候,一般都會加上

#ifndef    *** 

#define   ***  

…… #endif

這樣做的目的是為了防止標頭檔案的重複包含,具體是什麼意思呢?

它不是為了防止多個檔案包含某乙個標頭檔案,而是為了防止乙個標頭檔案被同乙個檔案包含多次

。具體說明如下:

檔案 

static void test_a() 

檔案 

#include "a.h" 

void test_b() 

#include "a.h" 

void test_c() 

這樣是沒有問題的,但下面這種情況就會有問題。

檔案 

static void test_a()   

檔案 #include "a.h" 

檔案 #include "a.h" 

檔案 #include "b.h" 

#include "c.h" 

void main() 

這樣就不小心產生問題了,因為 b.h 和 c.h 都包含了 a.h,那麼,在預編譯main.c 檔案的時候,會展開為如下形式:

預編譯之後的臨時檔案 

static void test_a()   

static void test_a()   

void main() 

在同乙個 .c 裡面,出現了兩次 test_a() 的定義,因此,會出現重複定義的報錯。

但是,如果在 a.h 裡面加上了 #ifndef……#define……#endif 的話,就不會出現這個問題了。

例如,上面的 a.h 改為:

檔案 

#ifndef  a_h 

#define a_h 

static void test_a()   

#endif

預編譯展開main.c則會出現:

預編譯後的臨時檔案 

#ifndef a_h 

#define a_h 

static void test_a()   

#endif 

#ifndef a_h 

#define a_h 

static void test_a()   

#endif 

void main() 

在編譯main.c時,當遇到第二個 #ifndef  a_h ,由於前面已經定義過 a_h,故此段**被跳過不編譯,因此,不會產生重複定義的報錯。這就是 #ifndef……#define……#endif 的精髓所在。

**:

C 預處理 一 include

c 的預處理 preprocess 是指在c 程式源 被編譯之前,由預處理器 preprocessor 對c 程式源 進行的處理。這個過程並不對程式的源 進行解析,但它把源 分割或處理成為特定的符號用來支援巨集呼叫。預處理器的主要作用就是把通過預處理的內建功能對乙個資源進行等價替換,最常見的預處理有...

異常處理機制

異常處理的三個步驟 檢查異常,丟擲異常,處理異常 異常處理基本原理 把需要檢測的程式放到try塊中,把異常處理的程式放在catch塊中。如果執行乙個函式出現了異常,可以丟擲異常資訊。然後查詢try塊下面的catch塊是否可以處理該異常。如果該函式不處理該異常,將傳遞給它的上一級函式 呼叫函式 如果它...

Handler處理機制

handler缺點 如果要運送兩種型別的資料 比如乙個bitmap,乙個object 就不能運送,但可以用bunder來傳輸 使用handler的步驟 1 建立乙個handler 2 在需要用到資料傳輸的地方通過handler得到message物件,通過message攜帶要傳輸的資料 3 直接通過h...