一、函数指针
我们知道,函数其实就是一段程序的子程序,是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。而指针是一个变量,用于存放地址的变量。那么函数指针,从语文的角度讲,中心词是指针,函数是修饰指针的定语。事实上C语言中也是这样的:函数指针,是一个指针,一个指向函数的指针。
这时大家可能就会纳闷了,函数有地址吗?怎么可以定义一个指针变量来指向函数呢,答案是肯定的,函数是有地址的。我们看下面这个例子
#include<stdio.h>
#include<stdlib.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);//输出函数名的地址
printf("%p\n", &test);//输出函数的地址
system("pause");
return 0;
}
它的运行结果如下图:
我们可以看到,函数是确实有地址的,并且可以看到函数名的地址和函数地址是相同的,那也就是说,函数名就是函数的地址。
那么函数有地址,我们就可以使用指针将它保存起来,这个指针就是我们所说的函数指针,亦即指向函数的指针。
我们通过两个例子具体体会一下函数指针的概念:
int (*test) (int);
上面这个代码是什么意思呢?没错,上面这个代码定义了一个函数指针test,它是一个可以指向函数的指针,这个函数的返回值类型为int ,有一个参数,参数的类型是int类型。
现在大家对函数指针有一个概念了吧。
二、函数指针数组。
上面我们介绍了函数指针,那么函数指针数组是什么,顾名思义,就是存放函数指针的数组。
我们还是通过一个具体的例子体会一下函数指针数组的概念:
int (*parr[10])(int);
上面这个代码看起来是不是很难懂?没关系,我们通过所学知识,一步一步会把它弄懂的。首先这个变量parr先和[ ]结合,说明它是一个数组。我们知道, 数组去掉数组名和[ ]即元素个数剩下的就是它的类型,那么这个数组去掉数组名和[ ]剩下的int( * )(int)就是它的类型了。这个类型怎么理解呢?没有错,就是我们上面讲的,指向函数的指针,也就是函数指针。那么我们把这些概念结合起来,上面这个代码的意思就是:一个数组parr,存放了10个元素,这10个元素的类型是一个函数指针int(*) int,这个函数指针是一个指向返回值类型为int,有一个类型为int的参数的函数的指针。
是不是也没有那么难懂?那么我们下面再看一个更有意思的概念。
三、函数指针数组的指针。
函数指针数组的指针,是什么意思呢?我们依旧按照语文的理解来理解一下。首先,它的中心词是指针,前面的数组作为定语修饰指针,那么数组前面的函数指针作为定语修饰数组。所以这个概念最终还是一个指针,事实上在C语言里面也是这样的。函数指针的数组的指针,是一个指针。指向什么的指针的?指向一个数组。这个数组里面存放的是什么呢?是函数指针。这样就串起来了。
我们依旧通过一个简单的例子来理解一下这个概念:
#include<stdio.h>
#include<stdlib.h>
void test()
{
printf("hehe\n");
}
int main()
{
/*定义一个函数指针存放函数test的地址*/
void(*ptest)() = test;
/*定义一个函数指针数组,用于存放函数指针*/
void(*ptestArr[5])();
/*将函数的地址放在函数指针数组首元素内*/
ptestArr[0] = test;
/*将函数指针数组的地址存放在指向函数指针数组的指针内*/
void(*(*pptestArr)[10])() = &ptestArr;
system("pause");
return 0;
}
可以看到,函数指针,函数指针数组,函数指针数组的指针这些概念都是环环相扣的。注释里面也可以看到, 倒数第三行代码就是我们正在阐述的概念:函数指针数组的指针,我们把它单独拿出来:
void(*(*pptestArr)[10])() = &ptestArr;
这么复杂的语句,怎么理解呢?不急,我们看下面这幅图,很好的解释了这个代码
现在大家对函数指针数组的指针也有了一定的了解了吧。
下面我们看两个比较有趣的代码:
(*(void(*)())0)();
void(*signal(int,void(*)(int)))(int);
是不是看到这个感觉百脸蒙圈?没关系,一一解释给大家。
第一行代码,void( * )()可以很容易的看出来它是一个函数指针类型,那么用()将它括起来放在0前面,就是将0强制转换为这个函数指针类型,表示一个地址,然后进行解引用,解引用之后变为函数的返回值类型,而这个函数是一个没有名字的函数,参数也没有。串起来就是返回值类型为((void()())0),无参的一个函数。
第二行代码,我们可以很容易的看出来,函数名是signal,两个参数为(int,void()(int)),一个是int类型,一个是函数指针类型,即void( )(int),那它的返回值类型是什么呢?没错,就是去掉函数名和参数之后所有的东西,也就是void( * )(int),所以这个函数按照我们一般的写法应该是这样子写的:
void(*)(int) signal (int,void(*)(int));
这样就可以看的很清楚了吧,也就是一个函数signal,返回值类型为void( * )(int),有两个参数,一个参数类型是int,一个是void( * )(int)。