(C語言)頭文件實現的函數

在C語言裏面,有時候爲了方便(方便的同義詞是偷懶),函數就直接在頭文件裏面實現了。那麼這樣子有什麼問題呢?

下面舉個例子,這個例子只有3個文件

/* fun.h */
#ifndef FUN_H
#define FUN_H
void base(){};
void fun();
#endif

/* fun.c */
#include "fun.h"
void fun()
{
    base();
}

/* main.c */
#include "fun.h"
int main()
{
    fun();
    return 0;
}

好,然後gcc一下

gcc -c main.c (通過)
gcc -c fun.c  (通過)
gcc -o main main.o fun.o (鏈接錯誤)

出現錯誤...“base()函數重定義!”

爲什麼重定義呢?因爲#include是預處理部分,在編譯之前由預處理程序在這個部分複製頭文件的內容過來。所以在編譯時候,main.o和fun.o文件都有base()函數的定義。那麼鏈接程序就不知道鏈接那個定義好了(二義性啊)

如何解決呢,爲了實現“聲明和實現分開”這個目標最好就是把這個base函數的函數體移到源文件裏面。如果由於某種原因真的要放在頭文件中...也可以。

用static聲明就可以了,靜態函數的作用域是文件,而不是全局。比如,上面的例子將頭文件裏面的void base(){}改成static void base(){},那就OK。

這個static在c語言中的用法可以google下,上面的資料好多很詳細滴。

順便說說頭文件的循環依賴的問題。

比如有三個頭文件a.h b.h c.h,a.h裏面有#include "b.h",b.h裏面有#include "c.h", c.h裏面有#include "a.h",那就會造成文件的循環依賴,後果是什麼呢?

比如有個文件a.c,上面有#include "a.h",那在a.c文件編譯之前,預處理程序就會不斷的把這三個頭文件的內容複製過來,超過了一定的數量,就會導致“頭文件數太多”的編譯錯誤。

解決方法呢,當然就是常見的#ifndef...#define...#endif組合了。不過要把前兩個寫在頭文件的開頭(一定是開頭),最後一個寫在最末尾。

這樣的話,第一次展開a.h b.h c.h的時候就已經定義了宏,到了c.h中的#include "a.h"時候,遇到了#ifndef,由於這個宏在上一次展開時已經定義了,所以這部分就跳過去了。也就是每個頭文件最多隻在每個源文件裏面包含一次。

但是即使編譯鏈接沒有問題,循環依賴也會降低開發效率,爲什麼?因爲文件都在依賴,比如某一天,要改變a.h的一部分內容,然後所有依賴於a.h b.h c.h的文件都得重新編譯...鏈接;所以現在的C++有“前向聲明”的技巧可以緩解這個問題。(緩解並不是解決。)而JAVA運用的import機制就很好的解決了這個問題,真正實現了“實現與聲明相分離”這個目標。

發佈了54 篇原創文章 · 獲贊 16 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章