C语言:浅谈函数指针、函数指针数组与函数指针数组的指针

一、函数指针
我们知道,函数其实就是一段程序的子程序,是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。而指针是一个变量,用于存放地址的变量。那么函数指针,从语文的角度讲,中心词是指针,函数是修饰指针的定语。事实上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)。

猜你喜欢

转载自blog.csdn.net/windyj809/article/details/80090098