C指针进阶(1)----函数指针

什么是函数指针?

程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。

以上内容是截取百度某用户的文字,根据他所描述,我们不难翻译为以下文字:

数组有内存地址,所以就有了数组指针或者指针数组,那么,函数在内存中也有与其对应的内存地址,所以当我们定义一个指针变量p和函数test,令p指向test,那么次指针变量p即为函数指针

那我们知道概念后,怎么去定义一个函数指针呢?

函数类型 (*指针变量名)(形参列表) = 函数名1;

从上面的定义中,我们不难看出函数指针并不复杂,接下来代码演示一下,我们就知道了

#define _CRT_SCURE_NO_WARINGS 1
#include<stdio.h>


int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 1;
	int b = 2;
	int(*p)(int ,int ) = &Add;
	//这里*p,因为p指向函数地址,所以,*进行解引用,所以,得到的就是add函数名,也就函数本身
	//所以(*p)(3, 5) = add(3,5)
	int result = (*p)(3, 5);
	printf("%d\n", result);

	return 0;
}

运行结果如下:

 接下来我们简单的解析这个函数指针

首先我们定义了两个变量a和b,并且为他们赋值1和2

紧接着,我们有了以下函数指针定义

    int(*p)(int ,int ) = &Add;

这个程序难点就是这个函数指针,我们来简单的 分析一下

首先,根据优先级情况,在这条语句中,我们不难发现,()的优先级是最高的,但是这里有两个括号,那么先执行哪一个?

非常简单,也很明显,执行(*p)这个括号,因为括号是左结合性,所以,这个语句,首先定义了指针变量p,然后才去执行第二个括号(int ,int ),那么,我们能够看出来,在这个括号中,有两个int,很显然,这是数据类型,那么说明这个括号是函数,只是,形参名省略了, 所以,我们得到了以下文字 

此语句定义了一个指针变量p,并且这个指针变量p指向一个函数(这个函数有两个形参,且都为int类型),那么,问题来了,这个函数返回类型是什么样的数据呢?int?double?float?char?结构体变量?指针变量?.........

于是,“函数类型”的作用就体现出来了。我想,我说到这里,大家都明白了这个
“数据类型”有什么用了吧。

没错,正如你所想

这个“数据类型”的作用就是用来声明这个函数是返回一个什么样类型的数据。

那么,文章写到这里,其实最难得部分就己经接近尾声了,

那么,问题超多的小明就又问了,“这个=&tets”有啥用?

其实,大家动脑子想一想就知道了,既然我定义了一个指针变量p,那么说明,我们必须要为p指针赋值一个地址,那么,这个地址从哪来?

很显然,就是从“=函数名”这部分来

我们上一篇文章就讲到过这么个结论

函数名 = &函数名

数组名 != &数组名

那么,既然我要为指针变量p赋值一个地址,那么,我传入某一个函数的函数名和&函数名,不就是将这个函数的首地址传给了指针变量p吗?,所以,我们就有了以下语句:

    int(*p)(int ,int ) = &Add;

现在我们就可以完美的解释,完整的写成以下文字:

定义了一个指针变量p,次指针变量p指向一个函数,且这个函数返回值为int类型的函数指针。

那么,写到这,我们成功的完成函数指针的定义,祝贺你,已经成功迈入指针进阶这个世界的第一步!!!

接下里,让我们迈出第二步,即函数指针的调用.

如何调用函数指针?

还是上面那个程序,我们来看一下这个程序是如何调用的。

    int result = (*p)(3, 5);

这条语句最简单的地方我想一定是int result,这只是一个变量定义而已,现在请将我们的目光看向(*p)(3,5),还是老样子,语句中出现括号,所以,先执行括号,但是同时出现两个,所以根据左结合性,我们即可发现,此语句首先执行“*p”,非常明显,这是指针基础的内容,只是简单地解引用而已,

注意!,这里*p的*是解引用,而上面的int(*p)(int ,int ) = &Add;中的*p中的*是定义指针变量,如何区别*是解引用还是定义指针变量,我在上一篇文章中已经做了阐述,若不懂,可以去看看。

所以,我们不难发现*p是解引用,而且p是指向test函数的首地址,那么,对它进行解引用,那么不就是test么?所以,*p = test,所以上面调用语句,就可以等价写成

test(3,5);

当我们写成这种形式的时候,我们居然发现了一个神奇的现象,test(3,5)这种函数调用形式居然是我们在C语言基础课程中“函数”这一章节的最基础,最简单的调用方式。

所以,其实,在我们C语言中很多东西它在底层,编译器中都是转换成指针形式的,为什么?

这是因为,指针的执行效率是非常非常非常高的,可以大大加大编译器,代码的运行效率!!!

这也就是为什么当大家去学习“函数”这一章的时候,会出现非常多的同学学不明白,有的同学就对这种调用方式有疑问“test(3,5)”为什么可以这么写?我想,很多老师都不会对这个进行解释,原因就是解释它的话,要牵扯到C语言指针进阶的内容

说了一些废话后,让我们整理一下函数指针调用方式的方法,截止到目前,我们知道了两种函数调用方式,即:

test(3,5);//最简单,也是最基础

(*p)(3,5);//只有当p指向test函数或者别的函数时,此语句成立,且(*p)左右两边括号万万不可省去,原因一会阐述。

接下来,让我们在学习一个函数指针调用方式,即:

    int result = p(3, 5);

没错,你没看错,p左边的*可以省去,

注意,只有在函数指针中,且指针变量指向函数,可以省去,只有这一种情况,在别的情况不可省去

这是因为,p既然指向tets首地址,那么就算我给它+1或者-1都会使程序报错,因为此时出现指针变量+整数,这是C语言当中不允许的,报错如下(VS2022)


 

所以,讲到现在,我想,你已经知道有三种方法可以调用函数即:

test(3,5);//最简单,也是最基础

(*p)(3,5);//只有当p指向test函数或者别的函数时,此语句成立,且(*p)左右两边括号万万不可省去,原因一会阐述。

p(3,5);//只有p指向某个函数时,才可以省去*

那么,写到这里,其实就把我的程序大体意思写完了,完整文档:

#define _CRT_SCURE_NO_WARINGS 1
#include<stdio.h>

/*
注意:
	数组名 != &数组名
	函数名 == &函数名
*/

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 1;
	int b = 2;
	int(*p)(int ,int ) = &Add;
	//第一种函数调用
	//这里*p,因为p指向函数地址,所以,*进行解引用,所以,得到的就是add函数名,也就函数本身
	//所以(*p)(3, 5) = add(3,5)
	int result = (*p)(3, 5);
	printf("%d\n", result);

	//第二种函数调用
	 	int(*p)(int ,int ) = Add;
	//因为这里函数名,代表的是函数地址,而p指向这个地址,所以,p的值与Add相同,
	//又因为*p,即为Add函数,所以,p与Add等价,所以,有以下表达式p(3,5),所以,这里的*只是解引用,意义不是很大。
	int result2 = p(3, 5);
	printf("%d\n", result2);

	//第三种函数调用
	int result3 = Add(3, 5);
	printf("%d\n", result3);

	return 0;
}

,那么,这就是函数指针,是不是很简单?很好玩?很有意思?

问题:

为什么int (*p)(int,int);中指针变量p括号不可省去?

解:

当我们去掉它两边的括号,即

int *p(int,int);

还是老样子,语句中出现括号,先执行括号,编译器一看括号内是两个形参,它就知道了这是用户定义了一个函数,那么p就会与()结合,形参函数,

注意,此时p就不是指针变量了,而是函数!!!

那么,这个函数的返回类型是什么?是 int *!!!

所以,一旦这样写,意思就变成了:

定义一个函数p,且返回类型是int *//int *是一个指向整数的指针,所以,它可以接收别的函数返回的整数地址,从而指向它。

而不是

定义一个指针变量p,且返回类型是int!!!

所以,我们可以发现,少一个符号,意思和功能居然相差千里。

我相信,如果你懂了这个,那么(*p)(3,5); 你也能懂!

那么,我们

int *p(int,int);这种形式定义为  指针函数

int (*p)(int,int);这种形式定义为   函数指针

总结:

在这个文章我们了解并掌握了C语言指针进阶----函数指针

并且我们也知道如何调用函数指针(三种方式)

test(3,5);//最简单,也是最基础

(*p)(3,5);//只有当p指向test函数或者别的函数时,此语句成立,且(*p)左右两边括号万万不可省去,原因一会阐述。

p(3,5);//只有p指向某个函数时,才可以省去*

并解释了“test(3,5);”在编译器的形式。

最后,我们知道了函数指针与指针函数的区别。

再次感谢您的观看,如果您觉得不错,清点赞,收藏,评论,让更多人看到,以此帮助到更多小白

数组与函数指针又会擦出怎样的爱恨情仇呢?小明想写一个计算器程序,但老师要求代码越少越好,又该怎么做呢?敬请期待下一篇内容----C语言指针进阶-----函数指针数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值