【C进阶】指针(二)

news2024/11/24 15:03:30

六、函数指针数组

数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组

eg:

int *arr[10]       //整形指针数组-数组-存放的是整形指针

char *arr[5]      //字符指针数组-数组-存放的是字符指针

那么把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢

int (*parr【10】)( )

parr先和【】结合,说明parr是数组,那么数组的内容是什么呢?

是int(*)()类型的函数指针(从函数指针+数组名【】

总结:

看pa和【】还是和*结合,

如果是和【】结合,那么pa就是数组

如果是和*结合,那么pa就是指针

eg:函数指针数组可以将类型为函数指针的元素放在一起

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int main()
{
	int (*pf1)(int, int) = &Add;
	int (*pf2)(int, int) = ⋐
	//数组中存放相同类型的多个元素
	int (*pfArr[2])(int, int) = { &Add,&Sub };
	//pfArr是函数指针数组-存放函数指针的数组
	return 0;
}

函数指针数组的用途:转移表

eg2:(计算器)

基础版:但是这个代码不好,如果想实现||,&&,&,|,>>,<<更多的功能还得重复写一些步骤,就很冗余

#include<stdio.h>
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 x, y = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret=%d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret=%d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret=%d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数:\n");
			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;
}

使用函数指针数组实现:

这样就统一起来了,更加简洁,方便,如果要加功能,只需要改菜单,数组定义,input的范围

#include<stdio.h>
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 x, y = 0;
	int input = 1;
	int ret = 0;
	int (*p[5])(int, int) = { NULL,&add,&sub,&mul,&div };//转移表(加个NULL是为了和数组下标统一)
	while (input)
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出程序\n");
		}
		else if (input <= 4 && input >= 1)
		{
			printf("请输入2个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);//*可不写,和函数指针一样
			printf("ret=%d\n", ret);
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}

	return 0;
}

注意:这样改的条件是:函数的返回类型和参数类型一样


七、指向函数指针数组的指针

(这部分内容不重要,但是可以拓展视野)

类比:指向指针数组的指针  int*(*p)【3】=&arr

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

eg:

int(*(*p)【5】)(int,int)=&pfArr   p是指向函数指针数组的指针

int(*     【5】)(int,int)=&pfArr   函数指针数组类型

int(*        )(int,int)=&pfArr         函数指针类型


八、回调函数

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

简单来说就是有两个函数,通过函数指针得到一个函数A(回调函数)的地址, 另一个函数

B(&A)实现间接调用A函数

注意:依赖函数指针才有回调函数

eg1:

计算器基础版简化:

这个部分每个case都在重复,那就用一个函数进行代替:

		case 1:
			printf("请输入2个操作数:\n");
			scanf("%d %d", &x, &y);
			ret = add(x, y);//sub/mul/div
			printf("ret=%d\n", ret);
			break;

运用回调函数进行改进:

#include<stdio.h>
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");

}
void calc(int (*pf)(int,int))
{
	int x, y = 0;
	printf("请输入2个操作数:\n");
	scanf("%d %d", &x, &y);
	int ret = pf(x, y);
	printf("ret=%d\n", ret);
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:\n");
		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函数回调add,sub,mul,div等函数,是通过函数地址回调函数,逻辑就是进入case语句calc函数开始执行,当执行到ret=pf(x,y),又回调add等函数,回调结束又回到calc函数中

注意:add,sub,mul,div这些函数才是回调函数,而不是calc函数


eg2:qsort快排:

对数据的排序方法有很多:冒泡排序,选择排序,插入排序,快速排序

为了对比qsort函数进行排序我们这里再来复习一下冒泡排序:

#include<stdio.h>
void print_arr(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void bubble_arr(int* arr, int sz)
{ 
	for (int i = 0; i < sz-1; i++)//趟数
	{
		for (int j = 0; j < sz-i-1; j++)//每一趟冒泡排序
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_arr(arr, sz);
	print_arr(arr, sz);

	return 0;
}

但是这个冒泡排序只能排序int类型的数据,下面就来介绍qsort快排:

qsort函数是一个库函数,底层使用的快速排序的方式,对数据进行排序,这个函数可以直接使用,可以用来排序任意类型的数据

头文件:#include<stdlib.h>

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

qsort(被排序数组的初始位置,要排序的数组的元素个数,一个元素所占字节,比较函数)

重点是最后一个参数,比较函数:

如果要升序(elem1<elem2),则return<0,就要return elem1-elem2

如果要降序(elem1>elem2),则return>0,就要return elem2-elem1

#include<stdio.h>
#include<stdlib.h>
int int_cmp(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);
}
int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), int_cmp);
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

比较不同类型的数据,方法是有差异的:

(1)排序整形数据,两个整形可以直接直接使用><比较

(2)比较结构体数据,两个结构体的数据可能不能使用>比较

(eg:字符串的比较得用strcmp)

 注意比较函数(__cdecl *compare )的返回值一定为int类型

比较函数就是回调函数,比较时没有直接用比较函数(__cdecl *compare ),而是通过函数指针传给qsort函数

为什么用void*类型的指针 ?

void*指针:无具体类型的指针

不能进行解引用操作,也不能进行+-整数的操作

它是用来存放任意类型数据的地址的

 结构体类型数据快排:

//结构体类型
struct Stu
{
	char name[20];
	int age;
};

//int cmp_stu_by_age(const void* e1, const void* e2)
//{
//	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
//}
//int cmp_stu_by_name(const void* e1, const void* e2)
//{
//	return *(struct *)e1 - *(int*)e2;
//}

int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
}
int main()
{

	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 12} };
	int sz = sizeof(arr) / sizeof(arr[0]);

	//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);

	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);

	return 0;
}

double类型的数据快排:这两种都可以

//浮点型数据
int cmp_double(const void* e1, const void* e2)
{
	return (int)(*(double*)e1 > *(double*)e2);
}
//int cmp_double(const void* e1, const void* e2)
//{
//	return *(double*)e1 < *(double*)e2?-1:1 ;
//}

本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 ! 

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

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

相关文章

2023国赛A题保姆级思路代码:定日镜场的优化设计

A题是一套传统的机理分析加规划求解题&#xff0c;首先我们要根据每个月21号的特定时间点建立一个太阳角度框架&#xff0c;根据题目所给出的公式计算效率&#xff0c;还有输出的热功率&#xff0c;然后根据月份求解各种效率&#xff0c;再把年份进行汇总&#xff0c;二三题都是…

阿里国际、亚马逊等跨境电商平台做测评怎么避免踩坑?

由于跨境电商的卖家越来越多&#xff0c;测评逐渐成为众多卖家的关注焦点&#xff0c;在各个跨境电商平台上&#xff0c;随着店铺数量的增加&#xff0c;同一商品之间的竞争也日益激烈&#xff0c;不同于国内的电商平台&#xff0c;跨境电商平台很少提供与卖家客服进行沟通的机…

论文简读 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

论文地址&#xff1a;https://arxiv.org/pdf/2106.09685.pdf 项目地址&#xff1a;https://github.com/microsoft/LoRA 全文翻译地址&#xff1a;https://zhuanlan.zhihu.com/p/611557340 本来想自行翻译的&#xff0c;但最近没有空 1、关键凝练 1.1 LORA是什么&#xff1f; …

序列号读取

1.序列号读取 1.1 应用示例目的与思路 获取光盘的外接圆&#xff1b;然后进行极坐标变换&#xff0c;获取字符所在的区域&#xff1b;最后进行字符分割、识别及其在原图上显示。 1.2 应用示例相关算子介绍 (1) mean_image(Image : ImageMean : MaskWidth, MaskHeight : ) …

SpringMVC的整合完成CRUD(增删改查)

SpringMVC是一种基于Java的Web框架&#xff0c;它是Spring框架的一部分。SpringMVC通过使用MVC&#xff08;Model-View-Controller&#xff09;设计模式来组织和管理Web应用程序的开发。 在SpringMVC中&#xff0c;Model代表数据模型&#xff0c;View代表用户界面&#xff0c;C…

2023年证券投资咨询行业研究报告

第一章 概况 1.1 定义 证券投资咨询业务是指取得监管部门颁发的相关资格的机构及其咨询人员为证券投资者或客户提供证券投资的相关信息、分析、预测或建议&#xff0c;并直接或间接收取服务费用的活动。 根据服务对象的不同&#xff0c;证券投资咨询业务可以分为&#xff1a…

2023年股票大宗减持研究报告

第一章 股票大宗减持概述 1.1 概念 大宗减持是指通过大宗交易的方式进行减持&#xff0c;即达到规定的最低限额的证券单笔买卖申报&#xff0c;买卖双方经过协议达成一致并经交易所确定成交的证券交易&#xff0c;其中A股单笔交易数量在30万股&#xff08;含&#xff09;以上…

JetBrains出品的IDE自动补全和提示文档设置

如题下图设置前景色和背景色&#xff1a; 效果如下图&#xff0c;标识1是Completion&#xff0c;标识2是Documentation&#xff1a;

人工智能应用加速落地,推动券商业务+IT双升级|爱分析报告

券商数字化转型已驶入快车道&#xff0c;多部政策文件相继发布&#xff0c;要求提升金融科技治理水平&#xff0c;加大科技资金投入&#xff0c;深化数字化转型。 与此同时&#xff0c;受宏观经济环境下行影响&#xff0c;券商同质化竞争加剧&#xff0c;传统经纪业务增长承压…

怎么把word文档转换成pdf?收藏这几个好方法

怎么把word文档转换成pdf&#xff1f;将Word文档转换为PDF格式是非常有用的&#xff0c;因为它可以确保文档的格式不会因为设备和软件的不同而发生变化&#xff0c;而且可以保护文档不被他人修改或篡改。此外&#xff0c;PDF文档通常比Word文档更小&#xff0c;更适合进行分享和…

Unity中Shader的屏幕抓取 GrabPass

文章目录 前言一、抓取1、抓取指令2、在使用抓取的屏幕前&#xff0c;需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的 前言 Unity中Shader的屏幕抓取 GrabPass 一、抓取 1、抓取指令 屏幕的抓取需要使用一个Pass GrabPass{} GrabPass{“NAME”} 2、在使用…

OpenCV4.8 GPU版本CMake编译详细步骤 与CUDA代码演示

导 读 本文将详细介绍如何使用CMake编译OpenCV4.8 CUDA版本并给出Demo演示&#xff0c;方便大家学习使用。 CMake编译详细步骤 废话不多说&#xff0c;直接进入正题&#xff01; 【1】我使用的工具版本VS2017 CMake3.18.2 OpenCV4.8.0 CUDA11.2 一般情况下VS版本≥VS2017均…

ICIF2023化工展首亮相,宏工科技解决方案助力制造升级

ICIF China 2023中国国际化工展览会于9月4日-6日在上海新国际博览中心举办。宏工科技携化工物料处理一站式解决方案首次亮相&#xff0c;同化工行业全产业链共叙物料处理自动化未来。 宏工科技是一家提供物料处理自动化设备、系统与服务的国家级高新技术企业&#xff0c;业务覆…

Java多线程4种拒绝策略

文章目录 一、简介二、AbortPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 三、CallerRunsPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 四、DiscardPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 五、DiscardOldes…

966SEO扫地僧站群·万能HTML模板[V1.9.1]

扫地僧站群万能HTML模板是一款站点管理软件,其主要特点是可以将原始的html模板放入程序中,无需编写任何标签,程序会全自动替换处理,从而快速构建出一个完整的网站,这种模式相对于传统的网站建设方式更加快速、简单,同时可以大幅度降低网站建设的成本和难度.服务器及域名量的配置…

【Linux】进程优先级,环境变量,进程地址空间

文章目录 1.进程优先级基本概念查看系统进程PRI and NIPRI vs NI修改进程优先级的命令其他概念 2. 环境变量基本概念查看环境变量方法常见环境变量测试PATH环境变量相关的命令环境变量的组织方式通过代码如何获取环境变量通过系统调用获取或设置环境变量环境变量通常是具有全局…

1DM+下载器_11.2.1魔改增强版下载

1DM「原&#xff1a;IDM」下载器是一款安卓端的下载工具&#xff0c;多语言解锁版直安装版本&#xff0c;号称是目前 Android 平台最快、最先进的下载管理器应用「支持通过Torrent下载」&#xff0c;而这个版本是改线程的最新idm版本&#xff0c;可用来下载视频、音乐、电影、T…

2023-9-8 求组合数(三)

题目链接&#xff1a;求组合数 IV #include <iostream> #include <algorithm>using namespace std;const int N 5010;int primes[N], cnt; bool st[N]; // 每个质数的次数 int sum[N];void get_primes(int n) {for(int i 2; i < n; i){if(!st[i]) primes[cnt]…

bean的管理-bean的获取

获取bean 默认情况下&#xff0c;在Spring项目启动时&#xff0c;会把bean都创建好&#xff08;但是还会受到作用域及延迟初始化的影响&#xff09;放在IOC容器中&#xff0c;如果想主动获取这些bean&#xff0c;可以通过如下方式 根据name获取bean Object getBean&#xff08…

智能机器人:打造自动化未来的关键技术

文章目录 1. 智能机器人的基本概念2. 智能机器人的关键技术2.1 机器视觉2.2 机器学习与深度学习2.3 传感器技术 3. 智能机器人的应用领域3.1 制造业3.2 医疗保健3.3 农业3.4 服务业 4. 智能机器人的未来趋势4.1 自主决策能力的提升4.2 协作与互操作性4.3 个性化定制4.4 环境感知…