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 . . .
*/
  以我的理解我认为已经实现了动态绑定。对于all函数来说,入口参数p决定了它会调用哪个函数,只有到函数执行期间才能确定,这就是动态绑定。

三、比较分析

       下面的代码是某网友用结构体和函数指针实现的:

代码②

/////////////////////////////////////////////////
#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++的一个功能而已,模仿了那么多,把一些不必要的东西也加上去了,反而混淆了动态绑定的根本。

posted on 2010-08-20 22:05  烛秋  阅读(1966)  评论(3编辑  收藏  举报