Skip to main content

C 語言中如何保護跨檔案的 exported symbol

有時候我們想把一個 module 分成好幾個 source/header 檔案, 因為這樣比較好管理, 也比較好看, 每個檔案也比較小, refactoring (re-compile) 的時間也比較短. 不過這樣的缺點是, 勢必有若干個原本是在一個 .c 檔裡的 static global variable 或 static function 會變成 global 的 variable 或 function. 這會增加別的 module (programmer) 誤用的風險, 在此提供一個保護這些你不願意讓其他 module 使用, 但卻可以在自己數個 .c 檔裡共用 variable (或 function) 的方法.

其實方法很簡單, 就是在 C 裡面借用 C++ 的 name mangling.

例如有一個 module A. 我用下面的 macro 來替每個希望只能用在 module 內部的 global variable (或 function) 加上一段文字:



在這邊用 "__this_(data | function)_should_not_be_(accessed | called)_outside_module_A" 這個字串來讓其他 programmer 很清楚的知道他不應該呼叫或存取這個 data (或 function), 但我又擔心這樣的防護還是不夠, 所以又加了一個 compiler predefined 的 macro, _INTEGRAL_MAX_BITS, 來增加使用的難度. 這個 _INTEGRAL_MAX_BITS 是由 Microsoft Visual C/C++ compiler 所定義的, 有可能會隨著不同版本的 VC++ 而不同, 也很有可能其他家的 compiler 不支援, 比方說 gcc, ARM CC 等等. 基本上, 這個 compiler predefined 的 macro 是看你所用的 compiler 而自由定義的.

=============================================================
註:

在一般的 VC 2005 使用上, _INTEGRAL_MAX_BITS 會被展開成為 32 這個數字.
=============================================================

=============================================================
註:

為了使用 _INTEGRAL_MAX_BITS 這種 compiler predefined 的 macro, 必須多使用一層 macro expansion, 這是因為 C preprocessor 的 ## (string concatenation) 會阻擋
preprocessor 的 prescan 的動作.
=============================================================

例如:

當有一個叫做 global_data 的 global variable 希望只有在 module A 內使用, 而不希望其他的 module 使用的話, 可以如下宣告:

int MODULE_A_INTERNAL_DATA(global_data);

經過 macro expansion 會變成:

int global_data__this_data_should_not_be_accessed_outside_module_A32;

而要存取他的話, 也很簡單, 就用

MODULE_A_INTERNAL_DATA(global_data) = 3;

就可以了.

function 的例子也是一樣.

當有一個叫做 global_func 的 global function 希望只有在 module A 內使用, 而不希望其他的 module 使用的話, 可以如下宣告 (定義也是一樣):

void MODULE_A_INTERNAL_FUNCTION(global_func)(int a)
{
\\...
}

呼叫這個 function 也很簡單:

MODULE_A_INTERNAL_FUNCTION(global_func)(3);

如此一來, 就可以約略的保住 module 內部的 internal data 及 function.

不過, 假如有其他的 module 也 include 了定義 MODULE_A_INTERNAL_(DATA | API) 的 header file 的話, 那不就破功了嗎? 所以最後, 我們要保護上面的這段特殊 macro, 否則其他的 module 只要 include 了這個 header file, 就可以透過這樣的
macro 來存取我的 global data 了, 所以在這個 header file 的最前面加上這段話:



並且在 module A 的所有 source files (.c) 的最前面加上下面這句話:



=============================================================
Note: 只能在 module A 的 .c 檔裡面加 #define THIS_FILE_IS_BELONG_TO_..., 不能在 module A 的任何一個 .h 檔裡面定義, 否則別的 module include 那隻 .h 檔就破功了.
=============================================================

這樣的話, 當有你不認識的人想要 include 你的 header file, compiler 就會馬上列印出一句大家都看得懂的話:

You should _NOT_ include this file directly.

所以, 不管是從哪條路上走, 都再再的告知其他 module 不可以來用我的 internal global variable or internal global function. 不是跟他說你不可以 include 這個 header file, 就是跟他說這個 variable, function 不可以被你存取或呼叫.

而且最棒的是, 上面的這些機制都是純 compile time 的, 也就是說完全沒有任何 runtime 的 overhead.

基本上, 我想應該可以達到為了模組化切割成多個檔案. 但又保護住自己 internal data 的好處.

Comments