大家好!这篇文章我们继续讲解指针的进阶内容。
1. 函数指针
函数指针顾名思义就是指向函数的指针。
首先看一段代码:
void test()
{
printf("hello\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
我们可以看到这两种写法都可以,函数没有首元素的概念。这两个地址都是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
void test()
{
printf("hello\n");
}
void (*pfun1)();
void *pfun2();
pfun1和pfun2哪个有能力存放test函数的地址?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
那我们该如何使用函数指针呢?
举个例子:
就是先解引用函数指针,然后在后面用函数调用操作符传参就行了。
其实这里的解引用是没有作用的。我们把Add放到pf里面,直接调用pf就类似于直接调用Add。
2. 函数指针数组
把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?是 int (*)() 类型的函数指针。
函数指针数组的用途:转移表
举个例子:(计算器)
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 menu()
{
printf("**********************************\n");
printf("***** 1. add 2. sub *****\n");
printf("***** 3. mul 4. div *****\n");
printf("***** 0. exit *****\n");
printf("**********************************\n");
}
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("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
这是我们一个加减乘除的计算器,但当我们需要添加更多的运算时,代码就会显得臃肿,我们该如何简化我们的代码呢?这里就可以用到函数指针数组。
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div};//pfArr是一个函数指针的数组,也叫转移表
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
break;
}
else if (input >= 1 && input <= 4)
{
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("ret = %d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
这样代码就得到了简化。
3. 指向函数指针数组的指针
指向函数指针数组的指针是一个 指针,指针指向一个 数组 ,数组的元素都是 函数指针。
如何定义?
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pa)(int, int) = Add;//函数指针
int (* pfA[4])(int, int);//函数指针的数组
int (* (*ppfA)[4])(int, int) = &pfA;//ppfA 是一个指针,该指针指向了一个存放函数指针的数组
return 0;
}
好了,理解到这里就行了,大家不用再继续套娃了。
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;
}
void calc(int (*pf)(int, int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
int main()
{
int input = 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;
}
在这里。我们把函数传给calc函数,在calc函数里用函数指针来接收,然后,我们用函数指针pf来调用相关功能的函数,这就叫做回调函数。这样,也简化了代码。
总结:
到这里,我们指针(提高篇)的基本概念已经讲解完。在第三节,我会讲解一些指针的题目,来帮大家巩固这块内容。如果大家认为我有哪些不足之处或者知识上的错误都可以告诉我,我会在之后的文章中不断改正,也请大家多多包涵。如果大家觉得这篇文章有用的话,也希望大家可以给我关注点赞,你们的支持就是对我最大的鼓励,我们下一篇文章再见。