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

posted @ 2014-04-26 10:38  Lhmily  阅读(1133)  评论(0编辑  收藏  举报