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

[CSDN][原创]

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


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

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

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

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

// .cpp
class 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.

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


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

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

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

// .cpp
extern "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);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值