如何把C++成员函数的地址传给C

来源:互联网 发布:网络黄金egd未来城app 编辑:程序博客网 时间:2024/05/12 21:14

[CSDN][原创]

无意中试到一种把C++成员函数的地址传给C的方法,能骗过编译器,但是运行时堆栈错误,没太大实际意义,记在这里,权当消遣。


通常情况下C++的成员函数的地址是不能传给C的,除非是静态成员函数。因为普通成员函数有一个隐含的this指针,是C中没有的,如果和C之间互相调用,由于两边参数和堆栈的维护不兼容,会导致堆栈混乱出错。如果传递普通成员函数地址到C中,那么编译器会报错。

例如在C中有一个函数CFun01,需要一个回调函数指针作为参数:

// .cint CFunc01(int (*cb)(int)){return cb(0x55AA);}

在C++的类中定义一个函数并传给C:

// .cppclass C{public:int Fun01(int a){printf("C::Fun01: a = 0x%X\n", a);return 0;}int Fun02(int a){CFunc01(&C::Fun01);return 0;}};

在Fun02处编译器会报告错误:

error C2664: 'CFunc01' : cannot convert parameter 1 from 'int (__thiscall C::* )(int)' to 'int (__cdecl *)(int)'
        There is no context in which this conversion is possible


通过下面的方法可以骗过编译器,强制将成员函数地址传给C:

// .cpp#include "stdarg.h"int GetFuncAddr(int t, ...){va_list va;va_start(va, t);int addr = va_arg(va, int);return addr;}extern "C" int CFunc01(int (*)(int));class C{public:int Fun01(int a){printf("C::Fun01: a = 0x%X\n", a);return 0;}int Fun02(int a){CFunc01((int (*)(int))GetFuncAddr(0, &C::Fun01));return 0;}};

在GetFuncAddr中,利用不定参数的特性,让编译器把函数地址放到堆栈上,然后从堆栈上再把函数地址用整数的形式取出来,这样就绕过了编译器的类型检查,很神奇的感觉。

以上方法在vs2005中测试通过,如果是VC6,把"&C::Fun01"换成"Fun01"即可。


不过虽然绕过了编译器,但是无法通过实际CPU运行,在实际运行中,C::Fun01可以被回调到,但该函数完成返回后堆栈就乱了。

运行结果:

C::Fun01: a = 0x55AA

运行结果是正确的,但运行后有错误提示:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

显然由于函数参数的压栈规则不一致,导致堆栈混乱了。


所以以上方法只能作为一种消遣,最终还是只能使用传统的静态成员的方法,无奈:

传统方法写在下面作为完结。

// .cint CFunc01(int (*cb)(void *, int), void *context){return cb(context, 0x55AA);}

// .cppextern "C" int CFunc01(int (*)(void *, int));class C{private:int Fun01(int a){printf("C::Fun01: a = 0x%X\n", a);return 0;}static int __Fun01(void *context, int a);public:int Fun02(int a){CFunc01(__Fun01);return 0;}};int C::__Fun01(void *context, int a){return ((C *)context)->Fun01(a);}int _tmain(int argc, _TCHAR* argv[]){C c;return c.Fun02(0);}


0 0
原创粉丝点击