C实现“动态绑定”
2010-7-26
烛秋
在论坛上看到这样一个问题:“用C能实现C++的动态绑定吗?”网址:http://topic.csdn.net/u/20100624/21/3d7eda37-cbf7-4e36-a549-f2d6f1a3eeed.html?47092。。现在看当时我的回复(ID:wuxupeng999),觉得理解上还是有些不足,这里再总结一下。
有网友给出了利用结构体和函数指针实现的方法。看了之后我觉得有点别扭,于是我自己再对动态绑定作了认真的理解,并做下总结。
一、什么是动态绑定
动态绑定就是某个对象要到程序执行时才知道是哪个对象。也就是说,最终编译成汇编指令的时候,可能是这样子的:"call [eax]",指令调用哪个函数,只有到程序执行的时候才会知道。在程序执行的时候,通过修改eax的值,使得程序调用不同的函数,这就是所谓的动态绑定。从汇编看来这是很平常的,例如,编写了一个函数,这个函数的入口参数eax是另一个函数地址,然后程序call [eax],这就动态了。当然也可以不用寄存器而用别的方式,例如call [地址a],地址a里边存放的值是某个函数的地址,修改地址a里的值就行了。
我觉得对于汇编来说,动态就是修改call后边的寄存器/地址的值,对于C来说就是修改指针所指向的函数地址,而指针也就是保存函数地址的变量,还是跟汇编一样。C++的动态绑定是高级语言的说法了,C++的动态绑定可以说是基类指针指向子类对象,然后通过基类指针调用子类实现的函数。而对于C和汇编来说,也就是把某个保存地址的变量的值修改一下。从这一点来说,用C实现动态绑定,没有必要搞什么模拟虚函数、虚函数表,直接来个(void*)指针,然后让它指向不同的函数,不过在函数调用的时候C编译器必须知道函数原型,可以使用typedef。
二、测试
代码①
/
#include <stdio.h>
#include <windows.h>
int test_add(int a, int b);
int test_sub(int a, int b);
void all(void *p);
int main ()
{
/*定义一个void类型指针*/
void *fp;
/*指向test_add*/
fp = (int*)test_add;
printf("test test_add:\n");
/*此时all执行test_add,输出7*/
all(fp);
/*指向test_sub*/
fp = (int*)test_sub;
printf("test test_sub:\n");
/*此时all执行test_sub,输出-1*/
all(fp);
system("pause");
return 0;
}
/*函数1*/
int test_add(int a, int b)
{
return a+b;
}
/*函数2*/
int test_sub(int a, int b)
{
return a-b;
}
/*依靠输入参数的不同,调用不同的函数。动态绑定*/
void all(void *p)
{
int a = 3;
int b = 4;
typedef int (*fp)(int a, int b);
int c = ((fp)p)(3,4);
printf("%d\n",c);
}
/*输出:
test test_add:
7
test test_sub:
-1
Press any key to continue . . .
*/
三、比较分析
下面的代码是某网友用结构体和函数指针实现的:
代码②
/
#include <stdio.h>
/
typedef struct base
{
void (*vfun)(void);
}Base;
typedef struct derived
{
Base b;
void (*vfun)(void);
} Derived;
/
void Bfun()
{
puts("Base vfcn");
}
void Dfun()
{
puts("Derived vfcn");
}
/
void Baseconstruct(Base* b)
{
b->vfun=Bfun;
}
void Derivedconstruct(Derived* d)
{
d->b.vfun=Dfun;
d->vfun=Dfun;
}
/
int main(void)
{
Base ba,*pb;
Derived de;
Baseconstruct(&ba);
Derivedconstruct(&de);
ba.vfun();//正常调用
de.vfun();
pb = &ba;//动态调用
pb->vfun();
pb = (Base*)&de;
pb->vfun();
getchar();
return 0;
}
/*
Base vfcn
Derived vfcn
Base vfcn
Derived vfcn
*/
代码②用结构体模拟类,然后还搞出了构造函数,模拟了很多C++的外表,但是代码②没有办法采用真正的虚函数表,所以在结构体里边保存了两个同样的指针。
这就是为什么需要:d->b.vfun=Dfun。“父类”(结构体)指针在调用vfun函数的时候,调用的结果就是这条指令的功劳。这里如果把Dfun改成其它的,那“父类”指针调用输出就是其它的了。这个“动态绑定”还是通过“子类”(结构体)对象来调用函数的。
动态绑定其实没必要搞得那么复杂,动态绑定只是C++的一个功能而已,模仿了那么多,把一些不必要的东西也加上去了,反而混淆了动态绑定的根本。