建議的方法的想法是使用間接函數調用,以便函數地址必須首先計算然後調用。 C預處理器用於爲實際功能定義一個代理函數,並且此代理函數提供確定代理函數提供訪問的實際函數的實際地址所需的計算。
代理設計模式允許你創建一個包裝類爲代理提供給其他 對象的接口:
關於其中有這樣一段話的代理設計模式的詳細信息,請參閱Wikipedia article Proxy pattern。作爲代理的包裝類 可以將附加功能添加到 的感興趣對象中,而無需更改對象的代碼。
我會建議一種實現相同類型的間接調用的替代方法,但它不需要使用C預處理器以這種方式隱藏實現細節,以便使源代碼的讀取變得困難。
C編譯器允許struct
包含函數指針作爲成員。是什麼樣的這個漂亮的是,你可以定義與功能的外部可見的結構體變量的指針一個成員尚未定義的結構時,在結構變量的定義規定的功能可以static
這意味着他們有文件能見度只有(見What does "static" mean in a C program。 )
所以我可以有兩個文件,一個頭文件func.h和一個實現文件func.c,它們定義了struct
類型,外部可見結構變量的聲明,static
修飾符使用的函數以及具有函數地址的外部可見結構體變量定義。
這種方法的吸引力在於,源代碼易於閱讀,大多數IDE將處理這種間接更好,因爲C預處理器沒有被用來在編譯時創建源,影響人們的可讀性和通過諸如IDE的軟件工具。
一個例子func.h文件,這將進行#included到使用的功能的C源文件,可能看起來像:
// define a type using a typedef so that we can declare the externally
// visible struct in this include file and then use the same type when
// defining the externally visible struct in the implementation file which
// will also have the definitions for the actual functions which will have
// file visibility only because we will use the static modifier to restrict
// the functions' visibility to file scope only.
typedef struct {
int (*p1)(int a);
int (*p2)(int a);
} FuncList;
// declare the externally visible struct so that anything using it will
// be able to access it and its members or the addresses of the functions
// available through this struct.
extern FuncList myFuncList;
而func.c文件示例可能看起來像:
#include <stdio.h>
#include "func.h"
// the functions that we will be providing through the externally visible struct
// are here. we mark these static since the only access to these is through
// the function pointer members of the struct so we do not want them to be
// visible outside of this file. also this prevents name clashes between these
// functions and other functions that may be linked into the application.
// this use of an externally visible struct with function pointer members
// provides something similar to the use of namespace in C++ in that we
// can use the externally visible struct as a way to create a kind of
// namespace by having everything go through the struct and hiding the
// functions using the static modifier to restrict visibility to the file.
static int p1Thing(int a)
{
return printf ("-- p1 %d\n", a);
}
static int p2Thing(int a)
{
return printf ("-- p2 %d\n", a);
}
// externally visible struct with function pointers to allow indirect access
// to the static functions in this file which are not visible outside of
// this file. we do this definition here so that we have the prototypes
// of the functions which are defined above to allow the compiler to check
// calling interface against struct member definition.
FuncList myFuncList = {
p1Thing,
p2Thing
};
使用這種外部可見的結構可能看起來像一個簡單的C源文件:
#include "func.h"
int main(int argc, char * argv[])
{
// call function p1Thing() through the struct function pointer p1()
myFuncList.p1 (1);
// call function p2Thing() through the struct function pointer p2()
myFuncList.p2 (2);
return 0;
}
的作爲通過Visual Studio 2005中對上述main()
發出sembler看起來像下面顯示通過指定地址的計算呼叫:
; 10 : myFuncList.p1 (1);
00000 6a 01 push 1
00002 ff 15 00 00 00
00 call DWORD PTR _myFuncList
; 11 : myFuncList.p2 (2);
00008 6a 02 push 2
0000a ff 15 04 00 00
00 call DWORD PTR _myFuncList+4
00010 83 c4 08 add esp, 8
; 12 : return 0;
00013 33 c0 xor eax, eax
正如你可以看到這個函數調用現在是間接功能通過內的偏移量規定的結構要求結構。
這種方法的好處在於,只要在通過數據區調用函數之前,就可以對包含函數指針的內存區域執行任何操作,正確的函數地址已放在那裏。所以你實際上可以有兩個功能,一個是用正確的地址初始化區域,另一個是清理該區域的功能。因此,在使用這些功能之前,您可以調用該功能來初始化該區域,並在完成該功能後調用該功能來清除該區域。
// file scope visible struct containing the actual or real function addresses
// which can be used to initialize the externally visible copy.
static FuncList myFuncListReal = {
p1Thing,
p2Thing
};
// NULL addresses in externally visible struct to cause crash is default.
// Must use myFuncListInit() to initialize the pointers
// with the actual or real values.
FuncList myFuncList = {
0,
0
};
// externally visible function that will update the externally visible struct
// with the correct function addresses to access the static functions.
void myFuncListInit (void)
{
myFuncList = myFuncListReal;
}
// externally visible function to reset the externally visible struct back
// to NULLs in order to clear the addresses making the functions no longer
// available to external users of this file.
void myFuncListClear (void)
{
memset (&myFuncList, 0, sizeof(myFuncList));
}
所以你可以做這樣的事情修改main()
:
myFuncListInit();
myFuncList.p1 (1);
myFuncList.p2 (2);
myFuncListClear();
但是你真的想要做的是有調用myFuncListInit()
在源某處那會不會是不遠的地方該功能實際上被使用。
另一個有趣的選擇是將數據區域加密,並且爲了使用該程序,用戶需要輸入正確的密鑰來正確解密數據以獲得正確的指針地址。
親愛的downvoter,我會很感激你的評論 – robert
SO是爲了幫助人們寫出更好的**代碼,而不是更糟。 – Olaf
@Olaf我正試圖保護一個商業軟件。 – robert