C语言--指针进阶2--函数指针、函数指针数组、指向函数指针数组的指针、回调函数、qsort()函数示例与模拟实现


六、函数指针

顾名思义,函数指针就是指向函数的指针,即存放函数地址的指针。

void test()
{
   
	printf("hehe\n");
}
int main()
{
   
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

输出结果:
在这里插入图片描述
我们发现函数名与取地址函数名内容相同,此处需要注意的是:
数组名 != &数组名
函数名 = &函数名

函数指针的定义:
与数组指针的定义类似,我们需要这样来定义一个函数指针:

int Add(int x , int y)
{
   
	return x+y;
}
int main()	
{
   
	int (*pf)(int , int) = &Add;//定义一个函数指针
}	

需要注意的是:
其中第一个int表示函数返回值类型,因为我们的Add()函数的返回值是int类型,所以此处用int。
(*pf)是表示pf是一个指针,为了让它先和*结合起来所以用括号圈起来,这和数组指针类似。
(int ,int)则表示Add()函数的参数类型。

举例:
对于void test(char *ptr){}这个函数如何定义一个关于它的函数指针呢?
函数指针定义:void (*pt)(char *) = &test;

既然如此,那么如何去使用这个函数指针呢?请看下面代码:

int Add(int x, int y)
{
   
	return x + y;
}
int main()
{
   
	int (*pf1)(int, int) = &Add;//定义一个函数指针
	int ret1 = (*pf1)(10, 6);//利用函数指针调用函数

	int (*pf2)(int, int) = Add;//因为Add和&Add一样,所以此处这样写也没问题!
	//由上面得出:Add === pf2
	//既然Add === pf2,那么下面的用法:
	int ret2 = pf2(10, 6);//这也是没问题的

	printf("%d\n", ret1);
	printf("%d\n", ret2);
	return 0;
}

对pf1解应用(*pf1)就找到了这个函数,再传参就可以调用该函数。
注意,一定要加括号括起来:(*pf1)
但是,这里的*号其实是摆设,直接使用pf1(10,6)也没啥问题!
当然啦,只有在函数指针这里这个*才可以省略!其他指针都不可省略解引用符号

输出结果:
在这里插入图片描述

阅读以下两个代码加深对函数指针的理解:

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

解释:

代码1:
(*(void (*)())0)();
从0处下手,(void (*)())0中,void (*)()表示一种类型,若为void (*p)()则可以更明显的看出这是一个函数指针变量,指向的函数返回值为void类型,不含参数。如果去掉p则表示一种函数指针类型(正如int a表示整型变量,而(int)0表示将0强制转化为int类型一样)。所以(void (*)())0其实表示的就是将0强制转化为函数指针类型,即0这个数字被强制认为是一个函数的地址。前面加上*号表示将0地址处的函数指针解引用,而由前面的该函数指针类型可知函数没有参数,所以整个的(*(void (*)())0)()表示:调用0地址处的函数,该函数没有参数,返回类型为void。

在这里插入图片描述
在这里插入图片描述

代码2:
void (*signal(int , void(*)(int)))(int);
先从signal入手,signal后面直接跟的圆括号,说明signal是一个函数,即:signal(int , void(*)(int)),signal函数有两个参数,第一个参数是int类型,第二个参数是void(*)(int)类型,这是一个函数指针,指向一个参数为int,返回类型为void的函数。
既然signal是一个函数,它的函数名与参数都有了,它的返回类型呢?
如果将上面signal(int , void(*)(int))这一块整体拿掉,剩下的void (*signal(int , void(*)(int)) )(int) --> void(*)(int)就是这个函数的返回类型,所以signal这个函数的返回类型也是一个函数指针,该指针同样指向一个参数为int,返回类型为void的函数。
综上所述,这是一个关于signal函数的函数声明!
在这里插入图片描述
为了方便理解,可以这样认为:void (*)(int) signal(int , void(*)(int));但是语法是不允许这样写的!那么能不能用一种方法让这个函数声明看起来更简洁明了呢?
这里需要用到typedef,它的作用是对类型重定义。比如typedef unsigned int u_int就是将unsigned int这个类型重定义为u_int,那么以后可以直接使用u_int来定义变量了,比如u_int a;
对于函数指针类型的重定义也可以这样做,它的形式是这样的:typedef void (*p_fun)(int);
在这里插入图片描述
于是这样,代码2可以简洁表示为:
在这里插入图片描述


七、函数指针数组

7.1函数指针数组的定义

既然有存放整型指针的数组–指针数组–int *arr[10];

那么肯定有可以存放函数指针的数组—函数指针数组—int (*pArr[2])(int, int)—pArr和[]先结合说明是一个数组,去掉数组名+[]后只剩下:int (*)(int, int),这就是这个数组的类型,这个类型是函数指针类型,所以这个数组也就是函数指针数组!

int Add(int x, int y)
{
   
	return x + y;
}
int Sub(int x, int y)
{
   
	return x - y;
}
int main()
{
   
	int (*pf1)(int, int) = Add;
	int (*pf2)(int, int) = Sub;//两个函数指针
	
	int (*pArr[2])(int, int) = {
    A
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值