深入解析c++静态联编和类的成员函数调用

来源:互联网 发布:js点击按钮刷新div 编辑:程序博客网 时间:2024/05/03 18:59

只要学了C++的人,肯定知道静态联编和动态联编,如果你不知道,ok那你学习之路还长。简单的静态联编的东西就不说了。先看下面程序。

class AA{
 public:
     void result()
       {
            std::cout << "Surprise?" << std::endl;
       };
 };
int main()
{
    AA *p = NULL;
    p->result();
    system("Pause");
    return 0;
}

上面程序运行会报错吗?

——————————————————————

如果你说运行一切正常并知道原因,ok。那就别往下看了,时间就是金钱。

确实,这个运行正常并输出  Surprise? 不信?你copy过去运行下试试。为啥啊。明明指针p的值是NULL,而你使用NULL指针去调用成员函数,明明会报内存错误的瑟。书上不是说了不能使用 NULL指针吗?嘿嘿,没错,确实不能使用NULL指针,但是这里,程序根本就没有用指针p的值,而是仅仅用到了它的类型做静态束定而已。

    要解此题首先要明确两个问题。

    1、静态联编的原理;2、成员函数的代码在运行期只有一份拷贝。

  静态联编简单的说就是在编译期就已经确定了要调用哪个函数了,这里的result()就是。同时要知道,类的成员函数在运行期只有一份拷贝在内存,不管类的实例有多少个,成员函数始终只有一份代码在内存,因此只要知道类的指针的类型之后,就可以定位到函数的入口地址,根本不关心该指针指向的是一个什么东西。成员函数和成员变量不一样,非静态成员变量是跟随类的实例走的。

  ok,明白上面两个问题之后,这个事情就好解决了。直接上汇编吧。

 汇编如下:

    AA *p = NULL;
00411ACE  mov         dword ptr [p],0
    p->result();
00411AD5  mov         ecx,dword ptr [p]
00411AD8  call          AA::result (41105Ah)

清楚了吧。在执行p->result()的时候只是把p的值移动到了一个暂存器里面,但是并没有用到这个值,后面就直接调用AA::result函数了,0x41105A正是该函数的入口地址。

ok,好了。不仅可以向以上说的去访问成员函数,甚至再过分一点((A*)0)->result();这样都可以。你再火一点把那个0换成任意一个地址都可以正确调用到那个函数,因为编译器在静态束定的时候只关心那个指针的类型。当然了,不可这样去访问类的成员变量,因为成员变量是在对象的内存布局里面的。

  值得说一点的是,如果你在result函数里面有涉及到类的成员变量的访问,那么这显然就会出错了,因为成员变量需要通过传进来的this指针(其实就可以理解成时p指针)去访问对象的内存的。然而此时p还没有指向一个有效的空间。故而出错。


文章出处:飞诺网(http://www.diybl.com/course/3_program/c++/cppjs/20091129/183676.html)

原创粉丝点击