指针进阶详解(下)

news2024/11/27 14:41:10

指针进阶详解(下)

  • 前言
  • 1. 函数指针
    • 1.1 两端有趣代码
  • 2. 函数指针数组
    • 2.1 函数指针数组的用途之一:转移表
  • 3. 指向函数指针数组的指针
  • 4. 回调函数
  • 5. 结尾

前言

在指针进阶详解(上)中,我们已经介绍了部分指针进阶相关知识,接下来我们将继续介绍指针进阶相关知识。

1. 函数指针

我们知道数组指针,是一个指向数组的指针。同理,函数指针是一个指向函数的指针。
既然是函数指针,就需要取出函数的地址。那函数的地址如何得到呢?

首先我们先看以下代码:

void test()
{
	printf("hehe\n");
}

int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

运行结果:
在这里插入图片描述
输出的两个地址就是test()函数的地址。那函数的地址如何保存起来呢?
下面再来看看这段代码:

void test()
{
	printf("hehe\n");
}

//下面pfun1和pfun2那个有能力存放test函数的地址呢?
void (*pfun1)();
void* pfun2();

首先,能存储地址,要求pfun1或pfun2是指针,那哪个可以?答案是:

pfun1可以存放。pfun1先和*结合,说明pfun1是一个指针;在和()结合,说明指针指向的是一个函数;最后和int结合,说明函数的返回类型是int。即,fun1指向一个返回int类型数据的函数。

1.1 两端有趣代码

//代码一
(*(void (*)())0)()
//解读:首先假设有一个数子:OX23FC11。如果仅是这个数字,我们可以认为他是一个地址,也可以是一个16进制数字
//同理,这里0也可以理解为一个地址,只是值比较特殊为0
//1.将0强制类型转换为void(*)()
//2.调用0处的函数

//代码二
void (*signal(int, void (*)(int)))(int);
//signal函数的一个声明
//1.signal函数有两个参数,第一个参数是int类型;第二个参数为int无返回值的函数指针类型
//2.signal函数的返回值是也是void(*)(int)函数指针类型,该函数指针指向的函数有一个int类型的参数,返回类型是void

代码二太复杂,如何简化呢?

typedef void(*pfun_t)(int)
pfun_t signal (int, pfun_t)

2. 函数指针数组

数组是一个存放相同类型数据的存储空间,我们已经介绍过指针数组,比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存放到一个数组中,那这个数组就叫函数指针数组。
那函数指针数组如何定义?

int (*parr1[10])();
//parr1先和[]结合,说明parr1是一个数组;数组的每个元素是int (*)()函数指针类型。

2.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;
}

int main()
{
	int input = 0;
	int (*p[5])(int x, int y) = { NULL, add, sub, mul, div };//转移表
	do
	{
		int x = 0; 
		int y = 0;
		int ret = 0;
		printf("XXXXXXXXXXXXXXXXXXXXXX\n");
		printf("XXXX 1.add  2.sub XXXX\n");
		printf("XXXX 3,mul  4.div XXXX\n");
		printf("XXXX 0.exit      XXXXX\n");
		printf("XXXXXXXXXXXXXXXXXXXXXX\n");

		printf("请选择:>\n");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = p[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误,重新输入\n");
		}
	} while (input);
	return 0;
}

3. 指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素是数组指针。

如何定义?

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void (*pfun)(char*) = test;
	//函数指针的数组pfunArr
	void (*pfunArr[5])(char*) = test;
	//指向函数指针的数组pfunArr的指针ppfunArr
	void (*(*pfunArr)[5])(char*) = test;
	return 0;
}
  • 不建议初学者直接上手写最终结果,而是根据上面过程一步步修改添加得到。

4. 回调函数

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

回调函数的应用

#include <stdio.h>
#include <stdlib.h>
//库函数中有一个函数qsort()是用来排序的
int cmp(const void* p1, const void* p2)
{
	//升序
	return *(int*)p1 - *(int*)p2;
	//降序
	//return *(int*)p2 - *(int*)p1;
}

int main()
{
	int arr[] = { 12,43,87,34,56,3,5,1,86 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp);//通过指针调用cmp函数,所以cmp()函数为回调函数
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

冒泡排序模拟实现qsort()函数

5. 结尾

本篇博客到此就结束了。创作不易,如果对你有帮助记得三连哦!感谢您的支持!!
指针进阶详解(上)
指针和数组笔试题解析

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于数据安全的风险评估(三):风险分析与评估

完成了资产识别、脆弱性识别及威胁识别后&#xff0c;我们可以采用适当的方法和工具确定威胁利用脆弱性导致安全事件发生的可能性。综合安全事件作用资产价值及脆弱性的严重程度&#xff0c;判断事件造成的损失及对组织的影响&#xff0c;即安全风险。 一 风险计算形式及关键环…

HTTP协议对比HTTPS协议

HTTP协议对比HTTPS协议 1. HTTP协议1.1 概述1.2 HTTP协议格式1.3 HTTP协议支持的方法1.3.1 GET方法1.3.2 POST方法1.3.3 其他HTTP方法1.3.4 GET对比POST 1.4 请求报文1.4.1 Content-Type&#xff1a;请求体中数据格式1.4.2 Cookie&#xff1a;浏览器缓存 1.5 响应报文1.5.1 状态…

LED驱动(原始架构优化:分层/分离)——STM32MP157

文章目录 优化思想&#xff1a;分层Demo的LED驱动程序led_opr.hboard_demo.cleddrv.cledtest.cMakefile编译测试 STM32MP157的LED驱动程序board_stm32mp157.cleddrv.cled_opr.hMakefiel编译测试 优化思想&#xff1a;分离Demo的LED驱动程序led_resource.hboard_A_led.cchip_dem…

MacOS系统(M1/M2)安装AI绘画StableDiffusion保姆级教程

TOC 安装完成后&#xff0c;推荐阅读这篇教程&#xff1a;AI绘画&#xff1a;Stable Diffusion 终极炼丹宝典&#xff1a;从入门到精通 实操环境&#xff1a; macOS 13 Arm64&#xff08;建议12以上的系统使用&#xff09; Apple M1 先来看几个样例&#xff1a; AI绘画S…

goland自定义代码模版

在 GoLand 中我们既可以修改已有代码模版&#xff0c;也可以新建属于我们自己的代码模版&#xff0c;按需配置。 一、修改已有代码模版 现在代码编辑页输入你想更改的代码模版&#xff0c;然后点击右下角的小灯泡按钮&#xff0c;接着选中编辑活动模版设置即可。 二、新建…

[机缘参悟-99] :关于局部最优与全局最优解的人生感悟

在没有获取全局信息之前&#xff0c;要获得全局最优解几乎是不可能的&#xff0c;最多是概率大一点而已&#xff0c;大多数时候&#xff0c;由于时空资源的限制&#xff0c;获得往往是局部最优解&#xff0c;局部最优解&#xff0c;放在全局&#xff0c;往往并非全局最优&#…

数学专题训练3 数论1

1. Problem - 27E - Codeforces 给定 n ( 1 ≤ n ≤ 1000 ) n(1 \le n \le 1000) n(1≤n≤1000)​​​&#xff0c;找到因子个数恰好为 n n n​​​ 个的最小正整数. 保证答案不大于 1 e 18 1e18 1e18. 和 反素数 的思路是一样的&#xff0c;深搜 这个是枚举当前数字可以…

Shiro反序列化漏洞(CVE-2016-4437)+docker靶场+工具利用

一、Shiro反序列化漏洞-CVE-2016-4437原理 将java对象转换为字节序列&#xff08;json/xml&#xff09;的过程叫序列化&#xff0c;将字节序列&#xff08;json/xml&#xff09;恢复为java对象的过程称为反序列化。 Shiro框架提供了“记住我”的功能&#xff0c;用户登陆成功…

JPA 概述及常用注解详解、SpringDataJpa 使用指南

JPA&#xff08;Java Persistence API&#xff09;是 Java 标准中的一套ORM规范&#xff08;提供了一些编程的 API 接口&#xff0c;具体实现由 ORM 厂商实现&#xff0c;如Hiernate、TopLink 、Eclipselink等都是 JPA 的具体实现&#xff09;&#xff0c;借助 JPA 技术可以通过…

Unity3D 入门

文章目录 拖拽快捷键资源商店地形构建器 拖拽快捷键 首先看到Scene界面&#xff0c;我们布置游戏场景中的游戏对象基本是在这个界面完成的 鼠标滚轮键按住&#xff1a;能够拖拽平面&#xff0c;不移动对象鼠标右键按住&#xff1a;能够旋转我们观察界面的视角&#xff0c;不移…

快速理解并实现权限控制

什么是权限控制? 权限控制是指在一个系统或应用中对用户或角色的操作进行限制和管理的过程。它用于确保只有经过授权的用户或角色能够执行特定的操作或访问特定的资源。权限控制是信息安全和访问控制的重要组成部分。 权限控制的主要目的是保护系统的安全性和完整性&#xf…

GEE:支持向量机(SVM)分类参数说明和官方案例

作者:CSDN @ _养乐多_ 本文记录了在GEE平台上进行支持向量机(SVM)分类使用ee.Classifier.libsvm的说明和官方案例。 文章目录 一、函数介绍1.1 GEE上说明1.2 函数说明二、官方案例一、函数介绍 1.1 GEE上说明 ee.Classifier.libsvm(decisionProcedure, svmType, kernelTyp…

CentOS5678 repo源 地址 阿里云开源镜像站

CentOS5678 repo 地址 阿里云开源镜像站 https://mirrors.aliyun.com/repo/ CentOS-5.repo https://mirrors.aliyun.com/repo/Centos-5.repo [base] nameCentOS-$releasever - Base - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$r…

Springboot JPA 集成多租户

背景&#xff1a; ​ iot-kit项目用的是jpa&#xff0c;不是mybatis&#xff0c;项目中需要引入多租户 参考文章&#xff1a; 【讲解多租户的实现与原理】 https://www.bilibili.com/video/BV1F84y1T7yf/?share_sourcecopy_web&vd_source981718c4abc87423399e43793a5d3…

pytest实现用例间参数传递的方式

pytest实现用例间参数传递的方式 一、通过conftest创建全局变量二、使用tmpdir_factory方法 我们在做接口自动化测试的时候&#xff0c;会经常遇到这种场景&#xff1a;接口A的返回结果中的某个字段&#xff0c;是接口B的某个字段的入参。如果是使用postman&#xff0c;那我们可…

前端精度丢失处理

前端操作数据时&#xff0c;如果数据超出一定范围会出现精度丢失的问题&#xff0c;这是因为&#xff0c;在传输过程中&#xff0c;数据类型被转换成Number&#xff0c;Number的精度范围在2^53之间&#xff0c;即 -9007199254740991 ~ 9007199254740991&#xff0c;超出范围就会…

Unity游戏源码分享-迷你高尔夫球游戏MiniGolfConstructionKitv1.1

Unity游戏源码分享-迷你高尔夫球游戏MiniGolfConstructionKitv1.1 有多个游戏关卡 工程地址&#xff1a;https://download.csdn.net/download/Highning0007/88052881

Unity游戏源码分享-射酒瓶游戏Demo

Unity游戏源码分享-射酒瓶游戏Demo 工程地址&#xff1a;https://download.csdn.net/download/Highning0007/88052883

财务报表制作:哪些软件值得推荐?

当今&#xff0c;企业需要准确、及时地制作各种会计报表&#xff0c;以便管理者更好地掌握财务状况。然而&#xff0c;使用传统的纸质方式进行制表常常会出现复杂、繁琐等问题&#xff0c;降低制表效率。因此&#xff0c;使用会 计软件成为了制表的首选。 传统制作财务报表的方…

前端理解的HTTP缓存(缓存的过程/策略/控制机制/作用和应用)

目录 一、HTTP缓存有什么作用&#xff1f; 二、 浏览器的缓存策略有哪些&#xff1f; 1、强缓存&#xff08;Expires、Cache-control&#xff09; 2、协商缓存&#xff08;Last-Modified、ETag&#xff09; 3、缓存过程是什么&#xff1f; 三、浏览器缓存控制机制有哪些&…