• __thiscalll C++底层识别成员函数


    问题描述:

    class myClass {
    public:
    	void SetNumber(int nNumber) {
    		m_nInt = nNumber;
    	}
    private:
    	int m_nInt;
    };
    
    int main(int argc, char* argv[],int _version)
    {
    	myClass Test;
    	Test.SetNumber(5);
    	return 0;
    }

      上述代码中,SetNumber是如何识别得出来m_nInt是类的成员变量???

    分析:

      main函数反汇编:

    ;省略进入main函数的部分汇编代码
    push    5
    lea     ecx, [ebp+var_4]    ;此处的[ebp+var_4]指的是Test对象的首地址
    call    j_myClass__SetNumber    
    ;省略调用Set_Number之后的汇编代码
    

      从上述汇编代码可以看得出来,在main函数调用成员函数SetNumber之前,编译器先将实参5压栈,然后将Test对象的首地址保持到寄存器ecx中,最后直接调用成员SetNumber。从汇编代码中,还可以看出SetNumber的函数名是经过了修改的。

      SetNumber函数反汇编:

    var_44= dword ptr -44h
    var_4= dword ptr -4
    arg_0= dword ptr  8 ;参数的地址
    
    ;下面是进入函数后,将上一层的调用者的信息保持,压栈。
    push    ebp
    mov     ebp, esp
    sub     esp, 44h
    push    ebx
    push    esi
    push    edi
    push    ecx
    lea     edi, [ebp+var_44]
    mov     ecx, 11h
    mov     eax, 0CCCCCCCCh
    rep stosd
    pop     ecx    ;恢复ecx,this指针。
    mov     [ebp+var_4], ecx    
    mov     eax, [ebp+var_4]
    mov     ecx, [ebp+arg_0]
    mov     [eax], ecx ;赋值,m_nInt=nNumber
    pop     edi
    pop     esi
    pop     ebx
    mov     esp, ebp
    pop     ebp
    retn    4  ;被调用者自己恢复栈
    myClass__SetNumber endp
    

      从上述的反汇编代码可以看出,在底层成员函数和普通函数是没有区别的。我们所看到的类和对象的概念是编译器提供的,也就是说在调用成员函数的时候,编译器做了一些“小动作”:利用寄存器ecx来保持对象的首地址(即this指针),并以寄存器传参的方式传递给成员函数,这种调用被称为__thiscall。(注意:并不是所有的成员函数调用都是通过ecx来实现的,得看具体的编译器)

      __thiscall和__stdcall都是被调用者自己恢复栈。但是两者的区别在于,前者使用到一个寄存器来传递对象的首地址,而非通过栈传递的方式。

  • 相关阅读:
    2016年工作中遇到的问题11-20
    分布式服务框架Dubbo入门案例和项目源码
    分布式服务框架Dubbo入门案例和项目源码
    小米网抢购系统开发实践和我的个人观察
    小米网抢购系统开发实践和我的个人观察
    大学毕业4年-回顾和总结(8)-全局观-互联网项目研发-不在其位亦谋其政
    大学毕业4年-回顾和总结(8)-全局观-互联网项目研发-不在其位亦谋其政
    某电商项目的若干技术问题
    某电商项目的若干技术问题
    ZOJ 3861 Valid Pattern Lock
  • 原文地址:https://www.cnblogs.com/lhmily/p/3690934.html
Copyright © 2020-2023  润新知