函数的本质

我们今天来分析一下函数的本质

知识扩展*:

程序的生成:**********     代码->二进制数据------>程序文件(保存在硬盘中)

程序的运行:**********     程序文件(硬盘)----->加载到内存中

一旦这个程序生成,二进制数据就不会再改变

准备工作

右键属性->C/C++->常规->禁用优化

做实验的时候,分析时可以调成release模式,能减少干扰

查看汇编代码

000511C2  jmp         000514B0  
    int add(int a,int b)
{
000514B0  push        ebp  
000514B1  mov         ebp,esp  
	return a + b;
000514B3  mov         eax,dword ptr [ebp+8]  
000514B6  add         eax,dword ptr [ebp+0Ch]  
}
000514B9  pop         ebp  
000514BA  ret  
	int a{ 100 };
000514DC  mov         dword ptr [ebp-4],64h  
	int b{ 200 };
000514E3  mov         dword ptr [ebp-8],0C8h  
	int c=add(100,200);
000514EA  push        0C8h  
000514EF  push        64h  
000514F1  call        000511C2  
000514F6  add         esp,8  
000514F9  mov         dword ptr [ebp-0Ch],eax  
第17行  第18行  push100和push200第7行  eax=&[ebp+8]
第19行  跳转到000511C2第8行   eax=eax+[(ebp+0c)]
000511C2 跳转到第4行第九行   epb 出栈
第4行  push  ebp第十行  返回000514F6
第5行  ebp=esp

从这里可以看出,ebp+8和ebp+c肯定是a和b的内存空间

函数名就是一个地址,那这个地址代表的内存空间里面存的是什么呢?存的就是代码

证明:我们打印一下函数名

    std::cout << std::hex << add;
	char* str = (char*)add;
	for (int i = 0; i < 30; i++)
	{
		printf("%02x\n",(unsigned char)str[i]);
	}

第一行打印结果为  009811C2,说明函数名是个地址

而之后打印出的字节和汇编代码中函数定义那几行显示的代码字节一样,说明地址里面储存的是代码,既然讲到了内存地址,就和指针有关系

函数指针

指针两要素:地址、类型,类型说明了如何表达这个内存地址

函数指针最重要的两个要素函数返回类型 参数列表

而函数体的内容重要吗?不重要,不管函数体内代码如何,只要确定了返回类型、参数列表就能通过call调用它,而不需要管函数体的具体代码实现

语法  函数返回类型 ( *函数指针变量名 )(参数类型  参数名  , 参数类型  参数名....)

示例:void (*ptr)(int a,int b)

如何使用

int add(int a,int b)
{
	return a + b;
}
int main()
{
    
	int (*ptr)(int a, int b) { 0 };
    ptr=add ;
	std::cout << ptr(1, 2);
}

这里就用ptr指向了add函数,如果指向的函数返回类型不一样怎么办呢?可以用到强制类型转换

char (*ptr)(int a, int b) { (char(*)(int,int)) add };

但是这样写太繁琐了,我们可以用typedef和using来改变

typedef char(*pdef)(int, int);
pdef ptr =  pdef add;

注意,这里的pdef是写在了*后面,括号里面,而不是写在最后

using puse = char(*)(int, int);
puse ptr =  (puse) add;

函数指针结合结构体

using puse = int(*)(int, int);
struct Role
{
	int Hp;
	int Mp;
};
int  add(Role r)
{
	return r.Hp + r.Mp;
}
int main()
{

	puse ptr=(puse) add;
	std::cout << ptr(1, 2);
}

这里的puse有两个参数,分别对应了struct里面的两个成员类型,重点在倒数第二行,传入的是两个参数,说明ROLE本质就是由两个参数构成

类的函数指针

当写类的函数指针必须把this指针也考虑进去,this指针来源于类的相关信息,所以也要把类的相关信息添加到函数指针

示例:typedef void (类名::*pfunction) (int a,int b)

#include <iostream>
class moveobject
{
public:
	void func1()
	{
		std::cout << 1 << std::endl;
	}
	void func2()
	{
		std::cout << 1 << std::endl;
	}
};
typedef  void (moveobject::* pfunction)();
int main()
{
	pfunction      pfunction1  = &moveobject::func2;
	moveobject* ptr = new moveobject();
	(ptr->*pfunction1)();
	system("pause");
}

基本用法:用函数指针指向要代表的类成员函数地址,然后调用这个函数指针里的函数

静态成玉函数用法和普通函数一样,因为它没有this指针,而且它就相当于一个普通函数

typedef  void ( * pstaticfunc)();
int main()
{
	pstaticfunc    pfunction   = &moveobject::func;
	moveobject* ptr = new moveobject();
	pstaticfunc();	
	system("pause");
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值