今天去面试,出了一个题目是用C跳转到0x00000000的位置。居然忘记了,晚上回来看一下,再一次彻底理解,希望不要再忘记了!特写下此篇来帮助大家理解
1指针函数:
指针函数从字面上理解就是一个带指针的函数。如 定义一个指针函数 int * test(int* x ) 就这么简单,最常见的一个函数就是malloc()函数啦,不要跟我说你不知道。
2函数指针:
函数指针本质是指针,然后可以跳到该地址执行。如 定义一个指针函数 int (*test)(int *)。这里的意思是将test定义为一个函数的指针,这个指针的指向的类型是参数是一个int*类型,返回值为int类型的函数。是不是和前面的指针函数很像,但是请看括号的位置,指针函数的*是和int结合,函数指针的*是和test结合。
有了上面的基础,我们可以开始将这个题目了,按照上面说的,我们可以定义一个函数指针P,然后把0x00000000赋值给P即可。如下:
void (*p)(void) = 0x00000000;
(*p)();
那我如何做到将其写成一步的形式呢?将0x0000000直接强转成某类型的函数指针,并跳转呢?
好我们先看看强转是怎么定义的,先几个简单的,比如:(char*)(data) , (int*)(data) , (long)(data)有没有发现什么规律?
其实强转某类型时就是将定义某类型的数据时,删除变量的名,并加上括号。举例如果是char *类型,你定义的时候是char * data, 如果是将 data强转成char *型呢?当然是
(char *)(data)了,明白我的意思了么?
我再来看一下如何强转函数指针,同理我们定义的时候是这样的void (*p)(void) ,那根据上面的规律强转就是将p这个变量去掉,加上括号。因此就变成了
(void(*)(void))(data),然后我们把data这个东西用0x0000000带入,于是就变成了(void(*)(void))(0x00000000)。好了0x00000000顺利转成了函数指针了,接下来就是执行它了
就相当于(*p)()这一步了。同理将上面那一部分东西代入即可。于是得到最终的形式是:(*(void(*)(void))(0x00000000))();大功告成。但是很多时候如果参数是void类型时,参数里的void可以不用写。所以大家可以看到常见的写法都是这样:(*(void(*)())(0x00000000))();但是如果是如果有参数值时,就必须将加上参数的类型。如
int (*test)(int *)
3函数指针数组
这个鸟东西一般情况下都见不到,但是在linux内核中,经常用它来做按照顺序初始化工作。最近也在学习linux内核就顺便讲讲。其实了解了函数指针以后就不难了,其实就是一个数组,里面存的都是一群函数的地址而已。那如何定义呢?
按照数组的定义方式应该是int data[];这样就可以了,那我们现在按照我们说的依样画葫芦。如果我定义的函数类型时 int (*p)(int,int);
那我们就可以写出函数指针数组方式应该是:int (*init[])(int,int);假设我定义的数组名字叫init。注:测试在linux下用gcc时,必须指定init的数组长度
好,我在linux写了个测试代码,来测试实现及调用过程,现在贴出代码来
1 #include<stdio.h>
2
3 int add(int x,int y)
4 {
5 printf("%d\n",(x+y));
6 return x+y;
7 }
8 int dec(int x,int y)
9 {
10 return x-y;
11 }
12 int main()
13 {
14 int (*init[2])(int ,int);
15 int z;
16 init[0] = add;
17 init[1] = dec;
18 z=(*init[1])((*init[0])(3,4),2);
19 printf("%d\n",z);
20 return 0;
21 }
如果再配合枚举,就可以模拟出linux内核中的函数形式了。数组里1,2的地方就可以用名字代替,增加可读性。这里就不举例了。
好了,就讲到这里了,如有任何疑问和错误,请联系我。