建议的方法的想法是使用间接函数调用,以便函数地址必须首先计算然后调用。 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