一、(*(void(*))0)()的理解。
导论:在一些复杂的代码工程中,经常出现类似于(*(void(*))0)()这种复杂的表达式,今天就带大家慢慢剖析这种表达式,教大家理解。
1.一个函数名的本质。
函数名的实质就是一个函数的首地址,当执行这个函数的时候,其实就是跳转到相应的地址去执行这个地址下装的二进制代码,可以通过以下程序了解:
#include <stdio.h>
int (*test)(int);
int Print(int num)
{
printf("The num is %d\n",num);
}
int main()
{
test = Print;
(*test)(1);
printf("The addr is %p\n", test);
(*(int(*)())0x804841d)(2);
}
运行的结果是:
The num is 1 The addr is 0x804841d The num is 2
对代码的分析:
-
首先通过int (*test)(int)定义一个int(*)类型的函数指针,然后将Print函数(实质上是一个地址)放到指针变量test中去,所以当执行(*test)(1)的时候实质上就执行了Print(1)。
-
其次,通过直接打印test这个指针变量可发现这个这个函数的首地址是0x804841d。
-
最后,为了证实这个地址是函数的首地址,所以我们把这个地址用强制转换成为int(*)()类型的函数指针,也就是(int(*)())0x804841d,最后,若是要引用这个函数,就必须对函数指针解引用,也就是(*fuc)(2),而这里的fuc指针就是(int(*)())0x804841d,所以(*(int(*)())0x804841d)(2)的意思就是将这串地址强制转换成为int(*)()类型的函数指针,然后再对这个指针解引用执行而已!
如此,(*(void(*))0)()这个问题的解析也同上了,(void(*))0实质就是将0地址强制转换成为void(*)类型的函数指针,然后再对这个指针解引用,也就变成了(*(void(*))0)()了。
二、使用Typedef对复杂指针变量的重命名。
1.Typedef的重命名类型可以使得函数指针的定义变得更加方便以及容易,而函数指针的重命名规则为如:
typedef int (*pF)(int);
此句的意思为将pF重命名为一个int(*)(int)的类型,所以上面的代码用重命名方式改写可得:
#include <stdio.h>
typedef int (*pF)(int);
int Print(int num)
{
printf("The num is %d\n",num);
}
int main()
{
pF test = Print;
(*test)(1);
printf("The addr is %p\n", test);
(*(pF)0x804841d)(2);
}
解析:
-
pF test = Print使用重命名以后可以直接pF定义test为int(*)(int)类型,并且将Print函数赋值到函数指针test中去,而后面的(*(int(*)())0x804841d)(2)可以变成(*(pF)0x804841d)(2)也比之前的简单许多,由于pF类型就是int(*)(int)类型,所以只需将这个地址强制转换成为pF类型并且解引用即可。
-
因此,(*(void(*))0)()也可以进行如下进行简化:
typedef void (*pF)();
(*(pF)0)();