關於c的標頭檔案的幾個問題
學c很多年來只是知道、那些律法般的規則,直到實戰才知道其真正的含義。
問題1. 關於防止巢狀包含巨集定義開關
在每個標頭檔案的開頭和結尾是標頭檔案的巨集定義開關:
#ifndef XXXX
#define XXXX
----
----
#endif
這是為了防止在一個原始檔中重複引用同一個標頭檔案造成的重引用。我可以理解在同一個問題中防止巢狀的功用,但是如果兩個原始檔同時引用同一個標頭檔案呢,畢竟XXXX是一個開關,當第一次已經被定義後,第二個引用的原始檔豈不是被開關擋在內容之外?但是我們平時使用並沒有出現這種現象,那麼原因何在?
看一個例子就知道了。
定義2個檔案a.h a.c
//a.h
1 #ifndef __aaa__ 2 #define __aaa__ 11 3 int a; 4 #endif
//a.c
1 #include "aa.h" 2 int main() 3 { 4 a=__aaa__; 5 return 0; 6 }
預編譯一下
#gcc -E a.c -o a.i
開啟a.i
1 # 1 "aa.c" 2 # 1 "<built-in>" 3 # 1 "<command-line>" 4 # 1 "/usr/include/stdc-predef.h" 1 3 4 5 # 1 "<command-line>" 2 6 # 1 "aa.c" 7 # 1 "aa.h" 1 8 9 10 int a; 11 # 2 "aa.c" 2 12 int main() 13 { 14 a=11; 15 16 return 0; 17 } 我們可以看到在預編譯完成的檔案中並沒有__aaa__,在我們引用的地方已經被替換成了11,這樣在後續的編譯連結階段,__aaa__是不存在的,也就不會有衝突的問題了,畢竟重定義的衝突要到連結階段才會別發現。於是問題解決。
問題2. 關於定義變數的問題
應該是我沒學好c,我一直以為要把全域性變數定義或宣告在標頭檔案中。指定我在大型工程中出現重定義的錯誤。
原因是顯然的,一個供超過2個原始檔引用的標頭檔案裡面定義或宣告的變數會被copy多份,這樣在連結的時候就會出現多重定義的錯誤。解決辦法是在某個.c檔案中定義和宣告全域性變數而在標頭檔案中加extern,這樣標頭檔案就可以被多原始檔引用而不會報錯了。
問題3. 關於宣告函式的問題
在我沒接觸大型c工程時,認為在標頭檔案中宣告函式是不容置疑的正確。而且就是把在原始檔中的宣告照搬到標頭檔案的那種。事實並非如此。
宣告函式同樣會遇到問題2中的情形。被引用的標頭檔案會被copy多次,進而產生多重宣告的錯誤。
解決辦法是,如果一個頭檔案只會被某個原始檔引用,那麼可以將它直接放在標頭檔案中,不用做修改,也不會出問題,這個同樣適用於上一個問題。對於一般的情形,有一種辦法是在宣告函式前加static或static inline。這樣就會使得函式只在引用的原始檔中起作用,使其scope變成檔案內可見而非全域性的。加inline就是將函式直接展開,加不加要看情況。
最後,送給自己:紙上得來終覺淺,絕知此事要躬行。