• __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都是被调用者自己恢复栈。但是两者的区别在于,前者使用到一个寄存器来传递对象的首地址,而非通过栈传递的方式。

  • 相关阅读:
    windows下搭建基于eclipse插件的GoLang开发环境
    vtk类之vtkImageReslice:基本算法,对体数据沿着轴进行切片
    vtk类之vtkTextureMapToSphere:纹理映射算法, 映射球体纹理
    wxpython 之 GDI 画刷Brush(三)
    c# 获取Rss数据
    C# 基础知识系列之面向对象基础
    SilverLight 得到文件绝对路径
    SilverLight C# 读取并修改App.config文件
    Silverlight 脱离浏览器
    C# 中New关键字的用法
  • 原文地址:https://www.cnblogs.com/lhmily/p/3690934.html
Copyright © 2020-2023  润新知