当前位置: 首页 > article >正文

指针的介绍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 占位

使数组下标对应菜单选项

这样以后,代码更加简洁,并且增添都很方便


http://www.kler.cn/a/525041.html

相关文章:

  • Go的内存逃逸
  • Redis学习之哨兵二
  • 批量卸载fnm中已经安装的所有版本
  • ios swift画中画技术尝试
  • Unbutu虚拟机+eclipse+CDT编译调试环境搭建
  • AI软件外包需要注意什么 外包开发AI软件的关键因素是什么 如何选择AI外包开发语言
  • Kafka 日志存储 — 磁盘存储
  • LeetCode:40. 组合总和 II(回溯 + 剪枝 Java)
  • Python3 【高阶函数】水平考试:30道精选试题和答案
  • SOME/IP--协议英文原文讲解4
  • 人工智能如何驱动SEO关键词优化策略的转型与效果提升
  • Unity阿里云OpenAPI 获取 Token的C#【记录】
  • Windows程序设计4:API函数URLDownloadToFile和ShellExecuteEx
  • Python3 【函数】:见证算法的优雅与力量
  • 论文阅读(十三):复杂表型关联的贝叶斯、基于系统的多层次分析:从解释到决策
  • 26.useScript
  • 如何将DeepSeek部署到本地电脑
  • Shell特殊状态变量以及常用内置变量总结
  • Kmesh v1.0 正式发布
  • 用JavaScript实现观察者模式
  • 小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)
  • 【16届蓝桥杯寒假刷题营】第2期DAY4
  • 安卓逆向之脱壳-认识一下动态加载 双亲委派(二)
  • 洛谷P3383 【模板】线性筛素数
  • 在Visual Studio Code自带的按键编译无法使用该怎么办
  • JavaScript_01