指针的介绍3后
1.函数指针变量
1.1函数的地址
void test(int (*arr)[2])
{
printf("zl_dfq\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
由上面的程序运行可知:
函数名就是函数的地址
&函数名也可以拿到函数的地址
1.2函数指针变量
1.2.1函数指针变量的引入
学习完整型指针变量,字符指针变量,数组指针变量之后,我们不难推出
函数指针变量就是一个存放函数地址的变量
void test(int a, int b)
{
printf("%d\n", a);
printf("%d\n", b);
}
int main()
{
void(*p)() = test;
return 0;
}
指针 p 就拿到了函数 test 的地址
1.2.2函数指针变量的书写
void(*p)(int a, int b) 去掉标识符 p 就是该指针的类型
即,p 指向了一个 void (*) (int a, int b) 型的函数
记忆方法:
(1)
void(*p)(int a, int b) 如果去掉第一个括号,变为
void* p(int a, int b)
那么,这将变成一个函数的声明:
函数名为 p ,形参为(int a, int b), 返回值为 void*
(2)
注意:
函数指针变量中,函数的
形参按原函数的顺序
可以只写类型,不写形参名
即 void(*p)(int a, int b) == void(*p)(int , int )
1.2.3函数指针变量的使用
通过函数指针调用 指针指向的 函数
void test(int a, int b)
{
printf("%d\n", a);
printf("%d\n", b);
}
int main()
{
void(*p)(int a, int b) = test;
(*p)(3, 5);
return 0;
}
(*p)(3, 5);
p拿到了函数test的地址
*p就拿到了函数, *p == test
此时传参即可
但注意*p要用()括起来,不然p会先于第二个()结合
由上面的程序运行发现:
(1)可以通过函数指针直接调用函数
(2)多次解引用指针, 没啥影响
实际上,
(1)编译器是直接通过地址找到函数,并调用的
此时并没有进行, *解引用操作
总结:
(1)
(*p)(3, 5)有解引用的书写 便于理解
p(3, 5)无解引用的书写 简化符号
(2)对于函数名来说,前面的&和*都会被忽略
1.3 typedef关键字
定义:typedef 关键字被用于为一个已存在的数据类型创建一个新的名称(别名)
用法:
typedef existing_type new_type_name;
注意:
对函数指针和数组指针创建别名时,标识符要放在(*)中
typedef void(*a)(int) ;//right typedef void(*)(int) a;//err
typedef int(*a)[10] ;//right typedef int(*)[10] a;//err
1.4两段有趣的代码
1.4.1第一段
(*(void (*)())0)();
解析:
(1)粉方框框起来的意思是
这是一个函数指针类型
(2)黑方框框起来的意思是
这将进行显示类型转换
(3)黄方框框起来的意思是
将 0 转换为一个函数指针
(4)红方框框起来的意思是
解引用函数指针,并调用该函数
1.4.2第二段
函数返回值是一个函数指针的时候,
函数的书写形式:
void (* signal(int) )(int); //right void (* )(int) signal(int);//err
所以请看题:
void (*signal(int , void(*)(int)))(int);
解析:
(1)黑方框框起来的意思是
这是一个函数
(2)粉方框框起来的意思是
函数的返回值是一个函数指针
实在是过于纷乱,使用typedef简化名称
typedef void(*fac)(int) ;
void (*signal(int , void(*)(int)))(int);
fac signal(int , fac);
代码就简单易懂啦!
2.函数指针数组与转移表
2.1相关含义
函数指针数组就是存放函数指针的数组
void(*)() p[10]; //err
void(*p[10])(); //right
第二行代码才是正确的函数指针数组的书写形式:
将 标识符[元素个数] 写入 函数指针的(*)中
函数指针数组的用途:转移表
转移表(Jump Table),也叫跳转表,是一种用于实现多分支选择结构的数据结构或技术
转移表通常用于替代复杂的if-else或switch语句
2.2举例之简易计算器的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
void menu()
{
printf("****************************\n");
printf("****************************\n");
printf("**** 1.Add 2.Sub *****\n");
printf("**** 3.Mul 4.Div *****\n");
printf("**** 0.OUT *****\n");
printf("****************************\n");
printf("****************************\n");
}
int main()
{
int input;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
int x = 0;
int y = 0;
switch (input)
{
case 1:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
int ret1 = Add(x, y);
printf("%d\n", ret1);
break;
case 2:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
int ret2 = Sub(x, y);
printf("%d\n", ret2);
break;
case 3:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
int ret3 = Mul(x, y);
printf("%d\n", ret3);
break;
case 4:
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
int ret4 = Div(x, y);
printf("%d\n", ret4);
break;
case 0:
printf("已退出计算器\n");
break;
default:
printf("选择有误,请重新选择\n");
}
} while (input);
return 0;
}
不难发现 switch 语句中 有大量的重复语句,
并且如果加上 & ^ ~ 等计算,会使代码更加冗余
此时想到转移表
只需要修改main 函数部分
int main()
{
int (*Funcs[5])(int, int) = { 0, Add, Sub, Mul, Div };
int input;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
int x = 0;
int y = 0;
printf("请输入两个操作数:");
scanf("%d %d", &x, &y);
int ret = Funcs[input](x, y);
printf("%d\n", ret);
}
else if (input == 0)
{
printf("已退出计算器\n");
}
else
{
printf("选择有误,请重新选择\n");
}
} while (input);
return 0;
}
(1)使用函数指针数组中存放每个函数的地址
(2)函数指针数组中,使用 0 占位
使数组下标对应菜单选项
这样以后,代码更加简洁,并且增添都很方便