指针(5)-学习笔记

1.函数指针变量

1.1函数指针变量的创建

什么是函数指针变量?

函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的。

我们可以测试一下函数是否有地址?

#include<stdio.h>
void test()
{
	printf("好好学习\n");
}
int main()
{
	printf("test:%p\n", test);
	printf("&test:%p\n", &test);
	return 0;
}

结果
test:00261410
& test:00261410

确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名的方式获得函数的地址。

&函数名和函数名都是函数的地址

如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针非常类似。

int ADD(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf3)(int, int) = &ADD;//pf就是函数指针变量
	return 0;
}

int (*pf3) (int x,int y)=&ADD x和y写上或者省略都是可以的。

1.2函数指针变量的使用

通过函数指针调用指针指向的函数。

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf3)(int, int) = Add;
	printf("%d\n", (*pf3)(2, 3));//传参过去的是2,3
	printf("%d\n", pf3(3, 5));//传参过去的是3,5
}

输出结果:

5
8

printf(“%d\n”, pf3(3, 5));//传参过去的是3,5

上面的代码是可以去掉*,以及iu可以正常编译。因为我们之前所学的函数调用是直接调用不需要使用指针。这两种都是可以用的,只是是不同的写法。

2.typedef关键字

typedef是用来类型重命名的,可以将复杂的类型,简单化。
比如,我们写无符号:

unsigned int num;

我们需要把无符号变量的前面都加上unsigned int,这很麻烦,那么我们可以使用:

typedef unsigned int uint;
//将unsigned int重新命名为unit

也可以将指针类型的重命名,比如:

typedef int* ptr_t;

重命名指针有一个特殊的点是:

	ptr_t p1, p2;
	//p1和p2都是指针

如果是:

	int* p1, p2;
	//p1指针,p2是int

也可以将数组指针重命名

typedef int(*parr_t)[10];//parr_t就是数组指针类型
int main()
{
	int arr[10];
	int(*pa)[10] = &arr;//pa是指针变量
	parr_t pb=&arr;//pb是数组指针变量
}

新的类型必须在*的右边。

函数指针类型的重命名也是一样的,比如,将void(*)(int)类型重命名为pf_t,就可以这样写:

typedef void (*pfun_t)(int);//新的类型名必须在*右边
typedef void (*pfun_t)(int);
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = Add;
	pfun_t pf2 = Add;//pf2是函数指针变量
}

3.函数指针数组

数组是一个存放相同类型的存储空间,我们以及学习了指针数组,
比如:

int * arr[10];
//数组的每个元素是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 (*pfArr[2])(int, int) = { Add,Sub };
	return 0;
}

pf1和pf2的类型都是一样的。所以可以放在一个函数指针数组中

4.转移表

函数指针数组的用途:转移表
举例:计算器的一般实现,完成加饭,减法,乘法,除法。

下面是没有使用指针的写法:

void 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 = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

使用指针函数的代码:

void 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 = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };//函数指针数组
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret=pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else
		{
			printf("输入错误,请重新选择\n");
		}
	} while (input);
	return 0;
}

大大减小了代码的重复,提高了效率。

5.回调函数

回调函数就是一个通过函数指针调用的函数

如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应。

int Add(int x, int y)
{
	return x + y;
}
void test(int(*pf)(int, int))
{
	int ret = pf(4, 5);
	printf("%d\n",ret);
}
int main()
{
	test(Add);
	return 0;
}

是通过函数指针来调用这个函数的。

回调似乎只是函数间的调用,和普通函数调用没啥区别。

但仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。

这样一来,我们只要改变传进库函数的参数,就可以实现不同的功能。

void 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;
}

void Calc(int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数>:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值