C语言指针(4):函数在指针中的进阶应用

news2024/11/18 1:33:24

1、回调函数

        回调函数就是⼀个通过函数指针调⽤的函数。函数参数的形式为函数指针类型。

当你把函数/函数的地址作为参数传递给相应函数是,如果这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用的,而是再特定的条件或输入操作下有另外的一方调用的,用于对该操作或条件的相应。

        结合上节课的计算器实例,进行如下修改可得:

#include <stdio.h>
void menu()
{
	printf(" 0、exit  \n");
	printf(" 1、Add   \n");
	printf(" 2、Sub   \n");
	printf(" 3、Mul   \n");
	printf(" 4、Div   \n");
}
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 calc(int(*pf)(int x, int y))
{
	int x = 0;
	int y = 0;
	printf("请输入两个操作数:");
	scanf("%d%d", &x, &y);
	int ret = pf(x, y);
	printf("%d\n", ret);

}
int main()
{
	int input = 0;
	int x, y;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
		case 0:
			printf("退出计算器\n");
			break;
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

综上:回调函数同样可以使代码更加简洁,这样的好处都是函数指针的应用所带来的。

qsort函数的使用

        c++官网给出的函数介绍:

函数原型:

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*, const void*));

void qsort (void* base                ----   元素的起始地址

                , size_t num               -----    元素个数

                , size_t size                -----    元素的字节大小

                ,int (*compar)(const void*, const void*))      ---   比较大小的函数

 使⽤qsort函数排序整型数据:

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

使⽤qsort排序结构数据:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu//结构体的创建
{
	char name[20];
	int age;
};

int cmp_by_name(const void* buf1,const void* buf2)//按姓名排序
{
	return strcmp(((struct stu*)buf1)->name, ((struct stu*)buf2)->name);
}

int cmp_by_age(const void* buf1, const void* buf2)//按年龄排序
{
	return (((struct stu*)buf1)->age) - (((struct stu*)buf2)->age);
}

void print_struct(struct stu s[], int sz)//打印结构体内容
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d\n", s[i].name, s[i].age);
	}
}
void test1()
{
	struct stu s[] = { { "zhangsan",33 }, { "lisi", 25 },{ "likezhen", 21 },{"zoutianlei",19}};
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_by_name);
	print_struct(s,sz);
}
void test2()
{
	struct stu s[] = { { "zhangsan",33 }, { "lisi", 25 },{ "likezhen", 21 },{"zoutianlei",19} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_by_age);
	print_struct(s, sz);
}
int main1()
{
	//test1();
	//test2();
	return 0;
}

        这里要注意的是,如果需要排序的内容是整型,可以直接返回两个元素相减的结果,但是要看清谁见谁,避免排反序;而如果排序的内容是字符串,可以使用strcmp函数来比较,大于零返回整数,小于等于零不进行操作。

冒泡函数与qsort的区别

        先来说一下冒泡函数:

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

        使用冒泡函数排序数组中的内容是比较常用的方法,但是冒泡排序只能排序整型数据,所以有一定的不足。而qsort可以对任意类型的数据进行排序,包括结构体。

        下面我们进行qsort函数的模拟实现,去探究函数内部的逻辑思维:

#include<stdio.h>

int cmp_int(const void* buf1, const void* buf2)//这里只是举了一个整型的例子,如果需要排序字符串的话,用strcmp函数即可。
{
	return (*(int*)buf1) - (*(int*)buf2);
}

void Swap(const void* base1, const void* base2, size_t width)//交换
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *((char*)base1 + i);
		*((char*)base1 + i) = *((char*)base2 + i);
		*((char*)base2 + i) = tmp;
	}
}
void bubble_sort(void* base,size_t sz, size_t width, int(*cmp)(const void* buf1,const void* buf2))//模拟函数,这里的函数指针是本函数的关键
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)//趟数
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)//一趟的交换次数
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
void print_arr(int arr[], int sz)//打印
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz,sizeof(arr[0]),cmp_int);
	print_arr(arr, sz);
	return 0;
}

部分拆解:

void Swap(const void* base1, const void* base2, size_t width)//交换
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *((char*)base1 + i);
        *((char*)base1 + i) = *((char*)base2 + i);
        *((char*)base2 + i) = tmp;
    }
}

这里还有一种写法:

void Swap(char* base1, char* base2, size_t width)//交换
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *base1;
        *base1 = *base2;
        *base2 = tmp;
        base1++;
        base2++;
    }
}

两种方式的目的都是将base1和base2拆解成一个字节的数据进行多次交换。

这样就避免了int型或更长整型倍数不符的问题。

 void bubble_sort(void* base,size_t sz, size_t width, int(*cmp)(const void* buf1,const void* buf2))//模拟函数,这里的函数指针是本函数的关键
{
    int i = 0;
    for (i = 0; i < sz - 1; i++)//趟数
    {
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)//一趟的交换次数
        {
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
            {
                Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
            }
        }
    }
}

将base强制转换为char*类型,可以是函数接收任何类型的数据通过单个元素的字节大小width都够准确找到下以个元素的起始位置。

本段代码需要细细品尝并进行模仿,慢慢体会其中的细节,掌握之后会提升很多。 

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

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

相关文章

workstation 用途

一 workstation 用途 强大的桌面虚拟化 允许创造多种操作系统可以不用重启就跨不同操作系统进行操作可以提供隔离的安全环境 连接到vsphere 可以远程登陆服务器管理物理主机和虚拟主机任何时间都可登陆提高虚拟机效率 为任何平台开发和测试 1&#xff09;借助一台单一本地…

【AI Agent系列】【MetaGPT多智能体学习】6. 多智能体实战 - 基于MetaGPT实现游戏【你说我猜】(附完整代码)

本系列文章跟随《MetaGPT多智能体课程》&#xff08;https://github.com/datawhalechina/hugging-multi-agent&#xff09;&#xff0c;深入理解并实践多智能体系统的开发。 本文为该课程的第四章&#xff08;多智能体开发&#xff09;的第四篇笔记。今天我们来完成第四章的作…

【解决】虚幻导入FBX模型不是一个整体

问题&#xff1a; 现在有一个汽车的fbx模型&#xff0c;导入虚幻引擎&#xff0c;导入后变成了很多汽车零件模型。 解决&#xff1a; 把“合并网格体”勾选上&#xff0c;解决问题。

蓝桥杯(3.2)

1209. 带分数 import java.io.*;public class Main {static BufferedReader br new BufferedReader(new InputStreamReader(System.in));static PrintWriter pw new PrintWriter(new OutputStreamWriter(System.out));static final int N 10;static int n, cnt;static int[…

装饰器模式:原来一直都在用

装饰器模式是一种结构型设计模式&#xff0c;它允许你动态地给一个对象添加一些额外的职责&#xff0c;而不需要使用子类来扩展功能。装饰器模式通过创建一个包装类来包裹原始类&#xff0c;然后在包装类中提供额外的功能&#xff0c;从而实现功能的动态添加&#xff0c;同时也…

【NR 定位】3GPP NR Positioning 5G定位标准解读(三)

目录 前言 5 NG-RAN UE定位架构 5.1 架构 5.2 UE定位操作 5.3 NG-RAN定位操作 5.3.1 通用NG-RAN定位操作 5.3.2 OTDOA定位支持 5.3.3 广播辅助信息支持 5.3.4 NR RAT相关定位支持 5.4 NG-RAN中与UE定位相关的元素功能描述 5.4.1 用户设备&#xff08;UE&#xff09; …

Mybatis Plus框架 基本语法

MybatisPlus 中文官网 依赖配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…

3.2日-线性模型,基础优化方法,线性回归从零开始实现

3.2日-线性模型&#xff0c;基础优化方法&#xff0c;线性回归从零开始实现 1线性模型衡量预估质量训练数据总结2基础优化方法3 线性回归从零开始实现 1线性模型 衡量预估质量 训练数据 总结 2基础优化方法 梯度下降是一种优化算法&#xff0c;常用于机器学习和深度学习中&…

【MATLAB】兔子机器人总系统_动力学模型解读(及simulink中的simscape的各模块介绍)

1、动力学模型 Rectangular Joint 控制平面上&#xff08;x&#xff0c;y轴&#xff09;的移动&#xff0c;去掉以后&#xff0c;机器人在原地翻滚不移动 Rigid Transform 坐标转换&#xff0c;B站视频已收藏 去掉&#xff0c;机体与地面贴合 此处的作用是设定机体的初…

Java多线程实现发布和订阅

目录 简介 步骤 1: 定义消息类 步骤 2: 创建发布者 步骤 3: 创建订阅者 步骤 4: 实现发布-订阅模型 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡…

javaWebssh教师荣誉库管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh在线授课辅导系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0…

leetcode刷题-110 平衡二叉树的判断(递归实现)

题目描述 解题思路 首先解释一下&#xff0c;为什么会做到这个题目&#xff0c;因为博主在按顺序做题的过程中&#xff0c;碰到了一个不会做的题目&#xff08;递归类型&#xff09;&#xff0c;就想着看看题解&#xff0c;发现了一个大佬的文章&#xff0c;就是专门讲的递归&…

Python推导式大全与实战:精通列表、字典、集合和生成器推导式【第115篇—python:推导式】

Python推导式大全与实战&#xff1a;精通列表、字典、集合和生成器推导式 Python语言以其简洁、优雅的语法而闻名&#xff0c;其中推导式是其独特之处之一。推导式是一种在一行代码中构建数据结构的强大方式&#xff0c;它涵盖了列表、字典、集合和生成器。本篇博客将全面介绍…

二维码门楼牌管理系统技术服务:文字规范与技术创新

文章目录 前言一、文字规范&#xff1a;确保信息传达的准确性二、技术创新&#xff1a;推动二维码门楼牌管理系统的升级发展 前言 在数字化时代的浪潮下&#xff0c;二维码门楼牌管理系统作为一种创新的城市管理手段&#xff0c;逐渐进入大众视野。这套系统不仅优化了城市空间…

131. 分割回文串(力扣LeetCode)

文章目录 131. 分割回文串题目描述回溯代码 131. 分割回文串 题目描述 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 示例 1&#xff1a; 输入&#xf…

使用最新Hal库实现USART中断收发功能(STM32F4xx)

目录 概述 1 认识STM32F4XX的USART 1.1 USART 功能说明 1.2 USART的中断 1.3 USART 模式配置 1.4 USART的寄存器 2 使用STM32CubeMX 生成工程 2.1 配置参数 2.2 生成工程代码 3 实现软件功能 3.1 软件功能介绍 3.2 认识USART Hal库 3.2.1 初始化函数组 3.2.2 发送…

Linux下进程相关概念详解

目录 一、操作系统 概念 设计操作系统的目的 定位 如何理解“管理” 系统调用和库函数概念 二、进程 概念 描述进程—PCB&#xff08;process control block&#xff09; 查看进程 进程状态 进程优先级 三、其它的进程概念 一、操作系统 概念 任何计算机系统都包…

产品营销展示型wordpress外贸网站模板

工艺品wordpress外贸主题 简约大气的wordpress外贸主题&#xff0c;适合做工艺品进出品外贸的公司官网使用。 https://www.jianzhanpress.com/?p5377 餐饮设备wordpress外贸主题 简洁的wordpress外贸主题&#xff0c;适合食品机械、餐饮设备公司使用。 https://www.jianzh…

如何使用Docker搭建StackEdit编辑器并结合内网穿透实现远程办公

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

oms-Diffusion:用户可上传服装图片与参考姿势图进行试穿,解决服装行业高昂成本问题

之前已经向大家介绍了很多关于虚拟试穿的项目&#xff0c;如谷歌的Tryon Diffusion, 阿里的Outfit Anyone, 亚马的Diffuse to Choose。东京大学的OOTDiffusion虚拟服装试穿工具。基于扩散模型的技术基本已经成为现在主流应用的基石。感兴趣的小伙伴可以点点击下面链接阅读~ 电商…