頭文件循環依賴


在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機制就很好的解決了這個問題,真正實現了“實現與聲明相分離”這個目標。

 
轉自:http://mlxia.javaeye.com/blog/351060


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM