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存在疑问.