C语言之深入指针(四)

C语言之深入指针

1 函数指针变量

1.1 函数指针变量的创建

整型指针是存放整型数据的指针
数组指针是存放数组地址的指针
那么函数指针变量就是存放函数地址的指针,可以通过函数的地址来调用函数

那么函数是否有自己的地址呢?我们可以写一段代码来看一下

#include <stdio.h>
void test()
{
}
int main()
{
	printf("test的地址是  %p\n", test);
	printf("&test的地址是 %p\n", &test);
	return 0;
}

代码运行结果如下
在这里插入图片描述
可以看到,函数确实是有地址的,并且与数组相似的是,函数名也可以是函数的地址,也可以使用取地址操作符 & 来得到函数的地址
那么用来存放函数的地址的就是函数指针变量,与数组指针变量创建方式相似

#include <stdio.h>

int Add(int x , int y)
{
	return x + y;
}

int main()
{
	int (*p1) (int,int) = Add;
	int (*p2) (int x,int y) = &Add;//x 和 y 可以省略不写
	return 0;
}

int  (pf3)  (int x, int y)
|    |    ------------
|    |      |
|    |   p指向函数的参数类型和个数的交代
| 函数指针变量名
p指向函数的返回类型
int (
) (int x, int y)是这个函数指针变量的类型

和数组指针变量相同的是,* 的优先级低于 ( )所以创建数组指针变量或者函数指针变量的时候,要将指针变量名用括号括起来

1.2 函数指针变量的使用

函数指针变量存放的是函数的地址,那么就可以使用它来调用函数

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*p) (int, int) = Add;
	int ret1 = (*p) (3, 5);
	int ret2 = Add(3, 5);
	printf("ret1 = %d\n", ret1);
	printf("ret2 = %d\n", ret2);
	return 0;
}

在这里插入图片描述

2 typedef 关键字

2.1 两段有趣的代码

先来看看两段出自《C陷阱与缺陷》的两端代码

1 (*(void (*)())0)();
2 void (*signal(int , void(*)(int)))(int);

代码一:
(int)10.5 这是将10.5这个浮点型的数据强制类型转换成int类型
其中 (void ( * ) ())的函数指针变量的类型
所以(void ( * )())0)是将0强制类型转换成(void ( * ) ())类型
然后使用指针变量来调用0地址处的函数
代码二:
首先,signal是一个函数名,(int , void( * )(int))是函数的形参,一个是int类型,另一个是为void(*)(int)的函数指针变量类型,也就是整个函数的参数
一个函数要有返回类型,函数名,参数
那么剩下就是这个函数的返回类型
综上所述这段代码是signal函数的声明

2.2 typedef

上述代码看起来很绕,让人不容易看懂
那么我们可以使用typedef关键字将类型重命名
例如:觉得unsigned int 太长了 可以将其简单化

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

typedef int(*parr_t)[5];//指针类型的重命名要将新的名字放在 * 旁边
typedef void(*pfun_t)(int);//新的类型名要在 * 旁边
//那么就可以将复杂的类型简单化
pfun_t signal(int, pfun_t);

3 函数指针数组

指针数组是用来存放指针地址的数组
那么函数指针数组就是用来存放函数地址的数组

3.1 函数指针数组的创建

与数组指针的创建类似

int (*parr)[5];//指针数组
int (*pf[5])();//函数指针数组

3.2 函数指针数组的使用

简易计算器

代码实现:

#include <stdio.h>

void menu()
{
	printf(" 1:Add 2.Sub");
	printf(" 3:Mul 4:Div \n");
	printf(" 0:exit \n");
	printf("*************************\n");
	printf("请选择:>>");
}

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();
		scanf("%d", &input);
		switch (input)
		{
		case 1L:
			printf("输入要计算是数:>>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输入要计算是数:>>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输入要计算是数:>>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输入要计算是数:>>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
		default:
			printf("非法输入,请重新输入\n");
		}
	} while (input);
	return 0;
}

在上述代码中,就会有很多重复的语句,那么我们可以将这几个函数放在一个函数指针数组中,代码会变得简洁一些
函数指针数组的⽤途:转移表

#include <stdio.h>

void menu()
{
	printf(" 1:Add 2.Sub");
	printf(" 3:Mul 4:Div \n");
	printf(" 0:exit \n");
	printf("*************************\n");
	printf("请选择:>>");
}

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 (*p[5])(int x, int y) = { 0,Add,Sub,Mul,Div };
	do
	{
		menu();
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}

	} while (input);
	return 0;
}

看完这篇博客之前,也可以了看看这篇深入指针(三),在其中介绍了指针数组 数组指针 字符指针变量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/146543.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

世界互联网大会|云轴科技ZStack受邀分享云原生超融合

11月8日至10日“世界互联网大会乌镇峰会”在浙江嘉兴的乌镇开幕&#xff0c;大会的主题为“建设包容、普惠、有韧性的数字世界——携手构建网络空间命运共同体”&#xff0c;全球各界代表就热点焦点问题展开讨论&#xff0c;反映产业各界对互联网发展的前瞻思考&#xff0c;引领…

UE4 / UE5 内存与性能优化

性能优化 资源压缩粒子优化NavMeshGenerate Overlap EventGCMarkTime光照优化Shader优化卡的时间长LOD官方CPU、GPU Insights、stat cpu、 ue4 memory report、inter GPA 、RenderDOC、减少模型面数等 资源压缩 在图片资源上右键选择Size Map可以看到资源所占大小&#xff0c;…

可以通过电脑远程控制安卓设备的软件

有些时候&#xff0c;我们需要用电脑远程控制安卓设备&#xff0c;比如远程维护门店设备、安卓系统的户外广告牌等等。我们来探索和比较几款允许用户通过电脑远程控制安卓设备的软件。 1、Splashtop Business Splashtop 是一种多功能远程访问解决方案&#xff0c;以其高性能流…

【大语言模型】Docker部署清华大学ChatGLM3教程

官方地址&#xff1a;https://github.com/THUDM/ChatGLM3 1 将代码保存至本地 方法1&#xff1a; git clone https://github.com/THUDM/ChatGLM3 方法2&#xff1a; https://github.com/THUDM/ChatGLM3/archive/refs/heads/main.zip 2 创建Docker文件 注&#xff1a;请先…

verdi merge fsdb出现信号冲突的解决办法

前段时间介绍了verdi用 Edit Virtual File的方式把几个fsdb文件merge起来的方法 由于当时实验的时候只用了两个小的fsdb文件&#xff0c;每个fsdb文件中包含的信号量也比较少&#xff0c;所以并没有发现问题 我是用 Edit Virtual FIle把dump不同hier的fsdb文件merge到一起&am…

2023年03月 Python(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 已知一个列表lst = [2,3,4,5,6],lst.append(20),print(lst)的结果是?( )(2分) A.[10,2,3,4,5,6,20] B.[20,2,10,3,4,5,6] C.[2,3,4,5,6,20] D.[2,3,4,5,6,10,20] 答案:C 第2…

uni-app报错“本应用使用HBuilderX x.x.x 或对应的cli版本编译,而手机端SDK版本是x.x.x不匹配的版本可能造成应用异常”

uniapp开发的一个跨平台软件&#xff0c;在安卓模拟器上启动的时候报警告&#xff1a; 官方给的解释&#xff1a;uni-app运行环境版本和编译器版本不一致的问题 - DCloud问答 解决办法有两个 方法一&#xff1a;添加忽略警告的配置 项目根目录下找到 manifest.json&#xf…

LeetCode 1334. 阈值距离内邻居最少的城市:多次运用单源最短路的迪杰斯特拉算法

【LetMeFly】1334.阈值距离内邻居最少的城市&#xff1a;多次运用单源最短路的迪杰斯特拉算法 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/ 有 n 个城市&#xff0c;按从 0 到 n-1…

【kafka】 查看节点的消息

对于初学者来说&#xff0c;可能想去节点看看有没有消息产生和消费&#xff0c;可以去kafka的bin目录下执行这个命令&#xff1a; kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic myTopic --from-beginning 这个命令可以理解为&#xff1a;生产过的消…

Python 爬虫之scrapy 库

文章目录 总的介绍相关模块 总的介绍 Scrapy是一个用于爬取网站数据的开源Python框架。它提供了一套强大而灵活的工具&#xff0c;用于从网站上提取所需的数据。Scrapy是基于Twisted异步网络库构建的&#xff0c;因此可以高效地处理大量的并发请求。以下是Scrapy的一些主要特点…

oracle-buffer cache

段&#xff0c;区&#xff0c;块。 每当新建一个表&#xff0c;数据库会相应创建一个段。然后给这个段分配一个区。 一个区包含多个块。 区是oracle给段分配空间的最小单位。 块是oracle i\o的最小单位。 原则上&#xff0c;一个块包含多行数据。 dbf文件会被划分成一个一个…

Clickhouse学习笔记(15)—— Clickhouse备份

手动备份 参考官网&#xff1a;Backup and Restore | ClickHouse Docs 简单来说&#xff0c;就是我们可以通过ALTER TABLE ... FREEZE PARTITION ...命令为表分区创建一个本地副本&#xff0c;然后这个副本硬链接到/var/lib/clickhouse/shadow/文件夹&#xff0c;因此其不会耗…

防爆五参数气象仪的科技力量

WX-FBQ2 随着科技的不断进步&#xff0c;气象监测设备也在不断升级和完善。 防爆五参数气象仪是一种可以同时监测温度、湿度、压力、风速和风向五个基本气象参数的仪器。它采用了气象监测技术&#xff0c;不仅可以实时监测气象数据&#xff0c;还可以对数据进行分析和处理。 …

【Java笔试强训】Day11(CM24 最近公共祖先、HJ86 求最大连续bit数)

CM24 最近公共祖先 链接&#xff1a;最近公共祖先 题目&#xff1a; 将一棵无穷大满二叉树的结点按根结点一层一层地从左往右编号&#xff0c;根结点编号为1。现给定a&#xff0c;b为两个结点。设计一个算法&#xff0c;返回a、b最近的公共祖先的编号。注意其祖先也可能是结…

上机实验四 图的最小生成树算法设计 西安石油大学数据结构

实验名称&#xff1a;图的最小生成树算法设计 &#xff08;1&#xff09;实验目的&#xff1a; 掌握最小生成树算法&#xff0c;利用kruskal算法求解最小生成树。 &#xff08;2&#xff09;主要内容&#xff1a; 利用kruskal算法求一个图的最小生成树&#xff0c;设计Krus…

U-Mail海外邮件中继帮您解决企业邮件退信难题

过去一年&#xff0c;国内外形势严峻复杂&#xff0c;但中国外贸顶住压力、爬坡过坎&#xff0c;进出口规模冲破40万亿元大关&#xff0c;高达42万亿元人民币&#xff0c;中国连续6年位居货物贸易第一大国。随着我国疫情防控措进入新阶段&#xff0c;“拼经济”正在成为各地的一…

量化交易:使用 python 进行股票交易回测

执行环境: Google Colab 1. 下载数据 import yfinance as yfticker ZM df yf.download(ticker) df2. 数据预处理 df df.loc[2020-01-01:].copy()使用了 .loc 方法来选择索引为 ‘2020-01-01’ 以后的所有行数据。通过 .copy() 方法创建了一个这些数据的副本&#xff0c;确…

OpenGL_Learn11(光照)

目录 1. 光照 2. 环境光照 3. 漫反射光照 4. 代码实战 1. 光照 在OpenGL中主要分以下几个光照类型 环境光照(Ambient Lighting)&#xff1a;即使在黑暗的情况下&#xff0c;世界上通常也仍然有一些光亮&#xff08;月亮、远处的光&#xff09;&#xff0c;所以物体几乎永远不…

VScode+python开发,多个解释器切换问题

内容&#xff1a;主要VScode使用多个解释器 环境准备 VScode编辑器&#xff0c;两个版本python解释器 python3.7.2 python3.11.6 问题&#xff1a; 目前我们的电脑安装了python3.7.2、python3.11.6两个解释器&#xff0c;在vscode编辑器中&#xff0c;无法切换解释器使用如…