C++的函数指针

2017/03/01 23:50
阅读数 25

看C++代码时看到了一段很新奇的用法, 回来剖析了一下, 感觉虽然原理很简单还是值得记录一下的。

代码大概是这个样子的:

class test {
public:
       void a1(){printf("test::a1\n");};
}

int main()
{
       test *t = new test();
       void (test::*a)(void);
       a = &test::a1;
       (t->*a)();
}

以前在C++中用函数指针时,从来都反射性的去用静态成员函数, 却从来都没想到原来普通的成员函数也是可以使用函数指针的。


仔细分析一下, 原理也早就知道。 无非就是this指针的事。


使用test对象的指针去调用某函数与普通的C函数指针惟一的区别就是, 会将test对象的指针作为this指针通过ecx寄存器来传入, 那么所有的C++类成员函数在本质上也是通过ecx的值来访问他们的成员变量。




在不反汇编的情况下, 为了更清晰的验证一下上述结论, 加入了如下代码:

void (*b)(void);
b = (void (*)(void))&test::a1;
b();


最后可以发现函数依然可以运行。


当然这里其实有点取巧,因为test::a1函数并没有访问类的成员变量, 所以我们可以将此成员函数直接作为普通的函数来调用。


如果test::a1函数访问成员变量的话, 由于C函数调用时并没有传入this指针, 那么test::a1函数必将会由于随机的ecx值而导致崩溃。


但是这段代码已经足够证明,&test::a1给出的是函数的绝对地址与普通的C函数的地址并无两样,那么调用b()和(t->*a)()的区别其实就在于是否传入了this指针。


如果再有点hack精神, 其实可以去将test::a1函数更改为访问成员变量, 然后在调用C函数前, 可以手动内嵌asm代码来将ecx值强制改为t的指针值, 如果效果一样, 那么即可完全证明上述结论。


btw, 其实最快的证明方法就是使用gcc生成一下汇编代码一看便知。 手动内嵌asm的事却颇像乌鸦去河边捡石子喝水的故事。

本文分享自微信公众号 - 重归混沌(findstrx)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部