【C语言进阶学习笔记】二、指针的进阶(2)(重点必看+代码演示+练习)

本篇文章的内容仍然为指针进阶的相关内容,继续上一篇文章的内容。【C语言进阶学习笔记】二、指针的进阶(1)(重点必看+代码图解+练习)
这是一篇干货满满的文章,希望有所收获~



5、函数指针

我们创建函数的时候,就会在内存中开辟一块空间,既然占用了内存空间,那就有对应的内存空间地址。
函数指针,顾名思义就是指向函数的指针。
在这里插入图片描述

注意:
& 函数名 和 函数名均表示函数的地址!
数组名 != &数组名
函数名 == &函数名


既然函数有地址,那么函数的地址该存放哪里呢?
在这里插入图片描述


思考:函数指针如何使用呢?
在这里插入图片描述

通过函数指针,我们可以找到函数,然后去调用这个函数。 函数指针是 &
函数名,而我们函数调用的时候可以直接使用函数名,那么这里通过函数指针调用函数也可以这样写:

在这里插入图片描述

既然这个地方的 * 可以省略,那么我们在使用的时候 * 可以用多个,也可以不要, * 号在这里就是一个摆设,这个地方放 * 是为了方便理解+
学习指针。


阅读两个有意思的代码:

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

这两个代码均是书籍《C陷阱与缺陷》中提及的内容,推荐阅读这本书。

这两个代码怎么阅读和理解呢?

//代码1 
(*(void (*) ())0)()

1.先将 void () ()理解清楚,这个是一个函数指针,指针指向的函数返回类型是void的。
2.再理解(void (
) ())0 我们之前的学习中学到过强制类型转换,需要将强制转换之后的类型用括号()括起来,这个地方就是将 0 强制类型转换成void() ()类型。 为什么要将0强制类型转换成void() ()类型呢? 原因:想要将0当做某个函数的地址
深入扩展:如果一个数字想要当作一个地址,直接使用这个数字肯定是不行的,而是要将这个数字转换成也给地址编号的类型。这也是代码1中为什么要将0强制类型转换的原因。
3.接着再看((void () ())0),对一个指针加*, 就是对其进行解引用操作,对函数指针解引用就是找到这个函数
4.((void () ())0)() ,((void () ())0)找到函数后,对其使用(), 就是调用函数,所以((void () ())0)(); 是一个函数调用。 整体理解下来就是:将0强制类型转换成一个函数指针void(*)
(),再通过对这个函数指针进行解引用操作,找到这个函数,对其进行调用!


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

1.signal是一个函数声明
2.signal函数的参数有两个,第一个是int类型,第二个是函数指针,该函数指针指向的函数的参数是int类型,返回类型是void(void()(int))
3.signal的返回类型是一个函数指针,该函数指针指向的函数的参数是int类型,返回类型是void(void(
)(int))

这种形式看起来就比较复杂和难以理解,我们可以用typedef类型重定义对其进行简化:

在之前的学习中,我们使用过typedef来定义过无符号整型 typedef unsigned int u_int;

但是我们并没有学过指针类型如何进行类型重定义,比如说 void()(int) ,如果我们要将其进行重定义,可以写成:
**typedef void(
)(int) pfun_t; 这种形式吗?**
在这里插入图片描述
我们尝试将其放到编译器下,就会发现编译器报错,显然这种方式是行不通的!


思考:那么可以将这个类型重定义后的名称类似与定义函数指针一样放到(* )里面吗?也就是typedef void(*pfun_t)(int);

在这里插入图片描述
写成这种形式后,编译器没有在报错或者警告,说明这种方式是对的,实际上正确的书写方式也正是这样!


//代码2
void(*signal(int, void(*)(int)))(int);
//那么这个类型可以简化成:
typedef  void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

虽然将一行代码变成了两行,但是简化后的代码更便于阅读和理解。
typedef 在进行类型重定义的时候,如果是函数指针类型,那么名称需要放到* 旁边,也就是说不能写成这种形式:typedef void(*)(int) pfun_t;
正确的形式是:typedef void(*pfun_t)(int);

深入扩展:当函数的返回类型是一个函数指针的时候,函数名需要放到函数返回类型-- - 函数指针内部,而不是直接放到返回类型— 函数指针后面。
void(signal(int, void()(int)))(int);


6、函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,比如︰

int* arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢 ?

int (*parr1[10])();
int* parr2[10]();
int (*)() parr3[10];

答案是:parr1 parr1先和[结合,说明parr1是数组。
数组的内容是什么呢 ?
是int(*)()类型的函数指针。


#include<stdio.h>
int Add(int x, int y)
{
   
	return x + y;
}
int Sub(int x, int y)
{
   
	return x - y;
}
int Mul(int x, int y)
{
   
	return x * y;
}
int Div(int x, int y)
{
   
	return x / y;
}
int main()
{
   
	//指针数组
	//int* arr[5];
	//需要一个数组,这个数组可以存放4个函数的地址---函数指针的数组
	int (*pa)(int, int) = Add;
	int (*p[4])(int, int) = {
    Add,Sub,Mul,Div };//函数指针的数组
	int i = 0;
	for (i = 0; i < 4; i++)
	{
   
		printf("%d\n", (*p[i])(2, 3));
	}
	return 0;
}

在这里插入图片描述


练习:

char* my_strcpy(char* dest, const char* src);

练习要求:

1.写一个函数指针pf,能够指向my——strcpy
2.写一个函数指针数组 pfArr,能够存放4个my_strcpy函数的地址

答案:

1.char* (pf)(char, const char*)
2.char* (pfArr[4])(char, const char*)


函数指针数组的用途:

转移表—《C和指针》这本书提及

请看下面简易计算器的例子:

#include<stdio.h>
void cal_menu()
{
   
	printf("*****************************\n");
	printf("*****   1.Add     2.Sub  ****\n");
	printf("*****   3.Mul     4.Div  ****\n");
	printf("******    0.exit     ********\n");
	printf("*****************************\n");
}
int Add(int x, int y)
{
   
	return x + y;
}
int Sub(int x, int y)
{
   
	return x - y;
}
int Mul(int x, int y)
{
   
	return x * y;
}
int Div(int x, int y)
{
   
	return x / y;
}
int main()
{
   
	int input;//根据菜单提示输入操作符
	int x = 0;
	int y = 0;
	do
	{
   
		cal_menu();
		printf("请选择操作符:>>\n");
		scanf("%d", &input);
		printf("请输入两个操作数:>>\n");
		scanf("%d%d", &x, &y);
		switch (input)
		{
   
		case 1:
			printf("%d\n", Add(x, y));
			break;
		case 2:
			printf("%d\n", Sub(x, y));
			break;
		case 3:
			printf("%d\n", Mul(x, y));
			break;
		case 4:
			printf("%d\n", Div(x, y));
			break;
		case 0:
			printf("退出!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
  • 26
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大家好我叫张同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值