老码识途之对象函数调用

上一期,我们讨论了普通函数的调用过程,如果没弄明白,看这里
今天所要讲的将是对象调用函数。

	class C{
	public:
		int a;
		int b;
		int c;
		void f(int t){
		 a = t;
		}
	};

int _tmain(int argc, _TCHAR* argv[])
{
 
	C c;
	c.f(1);
	c.b = 2;

	return 0;
}

由于之前的普通函数的调用的基础,所以,接下来主要分析下面几个问题

对象调用如何传递对象到函数内
静态对象函数调用和普通对象函数调用有什么不同

int _tmain(int argc, _TCHAR* argv[])
{
00654D30  push        ebp  
00654D31  mov         ebp,esp  
00654D33  sub         esp,0D8h  
00654D39  push        ebx  
00654D3A  push        esi  
00654D3B  push        edi  
00654D3C  lea         edi,[ebp-0D8h]  
00654D42  mov         ecx,36h  
00654D47  mov         eax,0CCCCCCCCh  
00654D4C  rep stos    dword ptr es:[edi]  
00654D4E  mov         eax,dword ptr ds:[0065F0E0h]  
00654D53  xor         eax,ebp  
00654D55  mov         dword ptr [ebp-4],eax  
 
	C c;
	c.f(1);
00654D58  push        1  
00654D5A  lea         ecx,[c]  
00654D5D  call        C::f (06510B9h)  
	c.b = 2;
00654D62  mov         dword ptr [ebp-10h],2  

	return 0;
00654D69  xor         eax,eax  
}

在这里插入图片描述
通过上图可以看到,对象函数的传参和普通函数相同,使用,压栈式,不知道大家有没有注意到,这里存在两个疑点,(1)明明我是对象调用函数,编译器却作为静态函数调用模式调用。(2)编译器将c的地址传递给了EXC寄存器。我们就带着这俩个疑点继续向下看吧。
下面是C类的汇编

		void f(int t){
00254420  push        ebp  
00254421  mov         ebp,esp  
00254423  sub         esp,0CCh  
00254429  push        ebx  
0025442A  push        esi  
0025442B  push        edi  
0025442C  push        ecx  
0025442D  lea         edi,[ebp-0CCh]  
00254433  mov         ecx,33h  
00254438  mov         eax,0CCCCCCCCh  
0025443D  rep stos    dword ptr es:[edi]  
0025443F  pop         ecx  
00254440  mov         dword ptr [this],ecx  
		 a = t;
00254443  mov         eax,dword ptr [this]  
00254446  mov         ecx,dword ptr [t]  
00254449  mov         dword ptr [eax],ecx  
		}

通过上图可以看到,首先,函数内部将ECX寄存器内容压栈,然后初始化函数栈内容,
然后将ecx的值传入到dword ptr [this],最后完成赋值,实际在这里有个疑问,之前人们都说对象函数在编译期间将this指针作为第一个参数进行传递,如果是按照第一个参数传递,应该压栈才对,为啥这里没有压栈而实用寄存器进行传递。等下看看用静态函数看看如何吧。

static类函数

在上面的代买基础上添加了一个静态类函数,同时在调用两个函数之间通过汇编代码修改了ecx值,这个防止上一个函数调用对这部分有影响

	class C{
	public:
		int a;
		int b;
		int c;
		static int d;
		void f(int t){
		 a = t;
		}

		static int t( ){
		 
		return d;
		}
	};

	int C::d = 12;
int _tmain(int argc, _TCHAR* argv[])
{
 
	C c;
	c.f(1);
	c.b = 2;
	{
		_asm{
		 mov ecx,10H
		}
	}
	c.t();


	return 0;
}

在这里插入图片描述
目前通过上图可以看到,静态函数的调用,连对象地址都没有传入。

		static int t( ){
00A54370  push        ebp  
00A54371  mov         ebp,esp  
00A54373  sub         esp,0C0h  
00A54379  push        ebx  
00A5437A  push        esi  
00A5437B  push        edi  
00A5437C  lea         edi,[ebp-0C0h]  
00A54382  mov         ecx,30h  
00A54387  mov         eax,0CCCCCCCCh  
00A5438C  rep stos    dword ptr es:[edi]  
		 
		return d;
00A5438E  mov         eax,dword ptr ds:[00A5F024h]  
		}

通过上面的return的汇编代码可以看到,它是直接返回的ds:[00A5F024h]的数据,
我们重新编译可以发现&c= 0x0053FBD8
在这里插入图片描述
而上面ds:[00A5F024h]这个属于栈中的数据,

小结

1)学到这里,我们应该明白了成员方法调用时,在编译过程中,编译器会将对象的地址通过ecx寄存器传入到函数中,并通过地址访问对象的成员变量。
2)成员方法的调用最终都是转换为了静态函数调用,通过传递对象的地址来辨别不同类型的方法
3)实际静态成员函数也好,普通成员函数也好,调用都是通过我们上节函数调用类似,换汤不换药
4)静态成员函数调用过程中,编译器并没有传递对象的地址到函数中,这也是为什么静态成员函数无法调用普通成员变量的原因

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值