2017-06-18 81 views
1

使用來自那些不同的定義假設你正在編寫內部使用的某些數據結構,並希望出口到用戶只是其中的一個子集的庫(或使用類似void *隱藏的確切類型)。庫中使用的所有結構和函數的定義位於標題library.h中,這將在構建庫時使用。使用外部頭文件與內部

是否也生成library.h的另一個副本,這種副本在構建過程中不會被使用,但只有鏈接到庫的用戶才被使用?

例如,假設庫的內部使用以下library.h

#ifndef LIBRARY_H 
#define LIBRARY_H 

struct myStruct { 
    int some_x; 
    void (*some_callback)(void); 
}; 

typedef struct myStruct *myStruct_t; 

#endif 

雖然我們想隱藏的myStruct給用戶的定義,所以我們出口頭library.h是:

#ifndef LIBRARY_H 
#define LIBRARY_H 

typedef void *myStruct_t; 

#endif 
+3

你應該考慮'library.h'和'library_impl.h'(或類似),與後者#包括前者。 –

+0

我傾向於組織我的項目,以便* public *標頭位於'include'子樹中,而* private *標頭(如@OliverCharlesworth'建議'* _impl.h')與源文件並排放置。然後安裝只安裝公共的。 –

+0

其他建議:如果你有*私有函數*以及在你的庫的翻譯單元中調用,但不打算通過使用代碼調用,谷歌爲符號可見性 - 例如gcc具有'__attribute __((visibility( 「hidden」)))' –

回答

2

它是好習慣也產生library.h的另一個副本,不會在生成過程中,但只有用戶鏈接到使用 圖書館?

號雖然最佳實踐爲你想要做什麼的細節可能是口味的問題,提供建設期間不使用的標題是客觀上不是一個好的做法:你可能會引進打字錯誤是永遠當你建立你的項目時會抓住你。

所以,沒有進入細節上,你應該如何組織說,你絕對應該做的是讓每個「私人」頭#include各自的「公共」頁眉和私人頭不重複的公共聲明。舉例來說,這將看起來像像:

library.h:

#ifndef LIBRARY_H 
#define LIBRARY_H 

typedef struct myStruct *myStruct_t; 
// there's absolutely no need to use void * here. An incomplete struct 
// type is perfectly fine as long as only pointers to it are used. 

#endif 

library_internal.h:

#ifndef LIBRARY_INTERNAL_H 
#define LIBRARY_INTERNAL_H 
#include "library.h" 

struct myStruct { 
    int some_x; 
    void (*some_callback)(void); 
}; 

#endif 

附加 「最佳實踐」 筆記:

  • 不要將指針隱藏在typedef之後。大多數C程序員都知道,一個指針是說明符的一部分,並希望明確看到指針時,有一個。解引用一些不看起來像指針只會造成他人閱讀的代碼混淆。您還可能混淆你的圖書館的消費者很快會myStruct_t展現的call-by-值語義。

  • 不要與_t後綴定義自己的類型。至少在POSIX中,這是爲實現(編譯器/運行時)保留的。定義一個與struct標籤相同名稱的類型沒有任何問題。

實施例與這些附加的建議:

library.h:

#ifndef LIBRARY_H 
#define LIBRARY_H 

typedef struct myStruct myStruct; 

#endif 

library_internal.h:

#ifndef LIBRARY_INTERNAL_H 
#define LIBRARY_INTERNAL_H 
#include "library.h" 

struct myStruct { 
    int some_x; 
    void (*some_callback)(void); 
}; 

#endif 
+0

簡單的'struct myStruct;'而不是'typedef'也可以。 – alk

+0

@alk肯定,如果你不需要typedef的名字。我更喜歡typedef名稱,它更接近於OP的原始代碼:) –

2

注意,C標準沒有按」不保證一個指向void的指針表示與指向結構體的指針兼容!因此:

typedef struct myStruct *myStruct_t; 
typedef void *myStruct_t; 

這兩個互不兼容,無法在嚴格遵循規則的程序中使用。


另一件事是你通常不應該隱藏指針,除非需要。考慮例如標準庫中的FILE。它的內容沒有在任何地方定義過,但是所有的功能都專門向它返回一個指針並且接受它的指針指針

你甚至可以用一個簡單的struct聲明,而不是定義:

struct myStruct; 

然後外部用戶可以定義一個變量,它的指針

struct myStruct *handle; 

或者,如果你想隱藏它確實是一個結構的事實,請使用typedef

typedef struct myStruct myStruct; 

然後外部接口的用戶可以定義自己的變量簡單地作爲

myStruct *handle;