函数名与函数指针

1.前言

对函数名和函数指针存在一些疑惑,如下:

  • p() / (*p)() / func()各自都是怎样对函数进行调用呢?
  • 函数名又到底是什么呢?

通过一个简单的测试程序,并对其进行反汇编,对其分析,对上述问题有了清晰的认识.

2.代码分析

先贴上测试代码和反汇编代码,以供分析.

2.1 测试源代码

​
typedef void (*pFunc)(void);

void func()
{
	int a = 1;
	printf("%d\n", a);
}

int main(void)
{
 	pFunc p;
 	p = func;
 	
 	/*1.call func*/
	func();
	p();
	(*p)();
	
    /* 2. print func,p,*p*/
	printf("func:%p, p:%p, *p: %p\n", func, p, *p);
	return 1;
}

​

2.2 反汇编代码

2.2.1 函数指针赋值

	​/*main() 函数的反汇编代码段*/
	pFunc p;
 	p = func;
   1041e:	f240 33f9 	movw	r3, #1017	; 0x3f9 	     //向寄存器的低16bit写入
   10422:	f2c0 0301 	movt	r3, #1 						 //向寄存器的高16bit写入,0x103f9即函数的入口地址.
   10426:	607b      	str	r3, [r7, #4]  					 //将函数的入口地址,写入到存储器地址 (r7, #4)中.从C语言的角度理解,即申请函数指针变量,并将func的值写入到此函数指针变量中.

从此段汇编代码分析可得,func函数名即标识func()函数的入口地址,是一个地址常量0x103f9.

2.2.2 函数调用的三种方式

	func();  //1.func()函数调用,直接将func标识符(0x103f8)作为立即数,bl跳转.至于0x103f9和0x103f8的区别在这里暂时不是讨论的重点,应该和处理器架构相关.
   10428:	f7ff ffe6 	bl	103f8 <func>    
  	 p();	 //2.从[r7, #4]中读取函数的入口地址到r3寄存器,并bl跳转.
   1042c:	687b      	ldr	r3, [r7, #4]
   1042e:	4798      	blx	r3
	(*p)();  //3.与p()做了完全相同的操作,也就是说在编译的汇编阶段,将p()和(*p)()翻译为相同的语句.
   10430:	687b      	ldr	r3, [r7, #4]
   10432:	4798      	blx	r3

从函数调用的代码分析可知:

  • func()是通过立即数直接跳转到函数入口地址
  • p(),(*p)()执行相同操作,都是读出变量p中的值(函数入口地址),并使用bl指令进行跳转.

2.2.3 函数名和函数指针属性

   printf("func:%p, p:%p, *p: %p\n", func, p, *p);
   10434:	687b      	ldr	r3, [r7, #4]   //*p的值即为[r7, #4]存储器地址中的值
   10436:	687a      	ldr	r2, [r7, #4]   //和*p取值完全相同,即为[r7, #4]存储器地址中的值
   10438:	f240 31f9 	movw	r1, #1017	; 0x3f9  //而func为地址常量,0x103f9.
  • 打印结果func, p, *p为完全相同的数值即 0x103f9.
  • func为地址常量,0x103f9.
  • p为函数指针变量, 存放在栈中,该变量中存放了函数入口地址0x103f9.因此对其直接访问,即内存地址中的值0x103f9
  • p只是对变量p的一种访问方式,从C语言层面理解,看似为指针的间接访问,实则不然,在汇编阶段,编译器将其解释为和p完全相同的取值操作.因此当p作为函数指针存在时,简单将p理解为p即可.

2.2.4 func函数的反汇编代码

void func()
{
   103f8:	b580      	push	{r7, lr}
   103fa:	b082      	sub	sp, #8
   103fc:	af00      	add	r7, sp, #0
	int a = 1;
   103fe:	2301      	movs	r3, #1
   10400:	607b      	str	r3, [r7, #4]
	printf("%d\n", a);
   10402:	6879      	ldr	r1, [r7, #4]
   10404:	f240 40a4 	movw	r0, #1188	; 0x4a4
   10408:	f2c0 0001 	movt	r0, #1
   1040c:	f7ff ef6c 	blx	102e8 <printf@plt>
}

3.总结

  • 函数名为地址常量,标识函数的入口地址.以前错误理解为在编译阶段被转化为函数指针.
  • 当p的数据类型为函数指针时,(*p)被翻译为和p相同的语义.以前错误地将(*p)理解为对函数指针p的间接访问,因此一直对p,*p,func存在疑问.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值