【C语言系列】深入理解指针(4)

深入理解指针(4)

  • 一、回调函数是什么?
  • 二、qsort使用举例
    • 2.1使用qsort函数排序整型数据
    • 2.2使用qsort排序结构数据
  • 三、qsort函数的模拟实现
  • 四、总结

一、回调函数是什么?

回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条
件发生时由另外的一方调用的,用于对该事件或条件进行响应。
上一篇文章我们实现了一个能够加减乘除和正常退出的计算器,阅读上篇文章:https://blog.csdn.net/2301_80179750/article/details/145286102?fromshare=blogdetail&sharetype=blogdetail&sharerId=145286102&sharerefer=PC&sharesource=2301_80179750&sharefrom=from_link
使用回调函数改造之前的代码:

#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;
}
nemu()
{printf("*************************\n");printf("  1:add           2:sub  \n");printf("  3:mul           4:div  \n");printf("  0:exit                 \n");printf("*************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//创建一个函数指针的数组
int(*pfArr[5])(int,int) = {NULL,Add,Sub,Mul,Div};
do 
{
nemu();
printf("请选择:");
scanf("%d",&input);//2
if(input >= 1 && input <= 4)
{
printf("请输入2个操作数:");
scanf("%d %d",&x,&y);
ret = pfArr[input](x,y);
printf("%d\n",ret);
}
else if(input == 0)
{
printf("退出计算器\n");
break;
}
else
{
printf("选择错误,重新选择\n");
}
}while(input);
return 0;
}

使用回调函数改造之后的代码:

#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 nemu()
{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 = 0;
int y = 0;
int ret = 0;
printf("请输入2个操作数:");
scanf("%d %d",&x,&y);
ret = pf(x,y);
printf("%d\n",ret);
}
int main()
{
int input = 0;
do
{
nemu();
printf("请选择:");
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;
}

通过观察上述代码我们可以得出结论:我们可以把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这里其实使用的就是回调函数的功能。

二、qsort使用举例

2.1使用qsort函数排序整型数据

qsort —— 用来排序的,库函数,直接可以用来排序数据,底层使用的是快速排序的方式。
注:qsort是库函数需要包含头文件<stdlib.h>
函数形式如下:

void qsort(void*base,//指针,指向的是待排序的数组的第一个元素size_t num,//是base指向的待排序数组的元素个数size_t size,//base指向的待排序数组的元素大小int(*compar)(const void*,const void*)//函数指针 —— 指向的是两个元素的比较函数);

官网如图:
在这里插入图片描述
在这里插入图片描述
官网链接:https://legacy.cplusplus.com/reference/cstdlib/qsort/?kw=qsort
qsort函数有实现者;qsort函数的使用者 —— 明确的知道要排序的是什么数据,这些数据应该如何比较,所以提供两个元素的比较函数。
使用qsort函数排序整型数据,代码如下:

#include <stdio.h>
#include <stdlib.h>
void print_arr(int arr[],int sz)
{
int i = 0;
for(i = 0;i < sz;i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
int cmp_int(const void*p1,const void*p2)
{
return*(int*)p1 - *(int*)p2;//可以调节降序
}// > ——> >0	//< ——> <0	//== ——> ==0
void test1()
{
int arr[] = {3,1,7,8,5,2,4,9,0,6};
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_int);
print_arr(arr,sz);
}
int main()
{
//写一段代码使qsort排序整型数据
test1();
return 0;
}

运行结果如下图:
在这里插入图片描述

2.2使用qsort排序结构数据

strcmp是按照对应着字符串中的字符的ASCII码值比值(是专门用来比较两个字符串的大小的),是库函数。
注:必须包含头文件<string.h>。
在这里插入图片描述
结构体访问成员操作符:

. ->

结构体变量.成员名
结构体指针->成员名
结构体访问操作符如何使用,代码如下:

#include <stdio.h>
struct Stu
{
char name[20];
int age;
};
void print(struct Stu*ps)
{
//printf("%s %d\n",(*ps).name,(*ps).age);
printf("%s %d\n",ps -> name,ps -> age);
}
//->结构体成员的间接访问操作
//结构体指针->成员名
int main()
{
struct Stu s = {"zhangsan",18};
print(&s);
return 0;
}

使用qsort排序结构数据,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//struct Stu
//{
//	char name[20];
//	int age;
//}s, s1, s2;//s1,s2,s是结构体变量
//typedef struct Stu
//{
//	char name[20];
//	int age;
//}stu;//stu是类型名
struct Stu
{char name[20];int age;
};
//这里的两个结构体元素怎么比较大小?
//1.按照名字比较 —— 字符串比较 —— strcmp
//2.按照年龄比较 —— 整型比较
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int cmp_stu_by_age(const void* p1, const void* p2)
{return((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void test2()
{struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);//qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{test2();return 0;
}

调试如下图:
在这里插入图片描述
通过调试,我们可以看出来qsort对结构体进行排序了。

三、qsort函数的模拟实现

使用回调函数,模拟实现qsort(采用冒泡的方式)。
qsort函数的模拟实现,代码如下:

#include <stdio.h>
void print_arr(int arr[],int sz)
{
int i = 0;
for(i = 0;i < sz;i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
int cmp_int(const void*p1,const void*p2)
{
return *(int*)p1 - *(int*)p2;
}
void Swap(char*buf1,char*buf2,size_t width)
{
int i = 0;
for(i = 0;i < width;i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void*base,size_t sz,size_t width,int(*cmp)(const void*p1,const void*p2))
{
//趟数
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);
}
}
}
}
//测试的是bubble_sort排序整型数据
void test1()
{
int arr[] = {3,1,7,8,5,2,4,9,0,6};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
print_arr(arr,sz);
}
int main()
{
test1();
return 0;
}

运行结果如下图:
在这里插入图片描述

四、总结

这篇文章详细介绍了C语言中的回调函数和qsort函数的使用,通过具体的代码示例展示了如何利用这些技术实现灵活的函数调用和数据排序。
回调函数
回调函数是一种通过函数指针调用的函数。在C语言中,函数指针允许我们将函数的地址作为参数传递给另一个函数,从而在特定的事件或条件下调用这些函数。文章通过一个简单的计算器程序展示了回调函数的使用。原始代码中,通过一个函数指针数组来选择不同的运算函数。改造后的代码中,使用了回调函数,将运算函数的地址作为参数传递给Calc函数,从而实现了更灵活的函数调用。这种改造不仅提高了代码的可读性,还增强了程序的灵活性和可扩展性。
qsort函数
qsort是C标准库中的一个通用排序函数,可以对任意类型的数据进行排序。文章首先介绍了qsort函数的基本用法,包括其参数的含义和如何定义比较函数。通过一个整型数组的排序示例,展示了如何使用qsort对整型数据进行排序。接着,文章进一步介绍了如何使用qsort对结构体数组进行排序。通过定义不同的比较函数,可以按照结构体中的不同成员进行排序,如按名字或年龄排序。这些示例展示了qsort函数的强大功能和灵活性。
qsort函数的模拟实现
文章最后通过一个冒泡排序的实现,模拟了qsort函数的行为。这个模拟实现使用了回调函数来比较元素,从而实现了对不同类型数据的排序。通过这个模拟实现,读者可以更好地理解qsort函数的内部工作机制,以及如何通过回调函数实现灵活的数据比较。
这篇文章通过具体的代码示例,详细介绍了C语言中的回调函数和qsort函数的使用。通过回调函数,可以实现更灵活的函数调用,提高代码的可读性和可扩展性。qsort函数则提供了一种通用的排序方法,可以对任意类型的数据进行排序。通过这些技术,读者可以更好地理解和应用C语言中的函数指针和排序算法。

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

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

相关文章

vim的多文件操作

[rootxxx ~]# vim aa.txt bb.txt cc.txt #多文件操作 next #下一个文件 prev #上一个文件 first #第一个文件 last #最后一个文件 快捷键: ctrlshift^ #当前和上个之间切换 说明&#xff1a;快捷键ctrlshift^&#xff0c…

Salesforce Too Many Email Invocations: 11

在 Salesforce 中&#xff0c;“Too Many Email Invocations: 11” 错误通常表示您的组织在单个事务中超过了 Apex 电子邮件调用的限制。Salesforce 设置这些限制是为了防止滥用并确保公平使用。以下是解决该问题的方法&#xff1a; 理解限制 Salesforce 允许每个事务中最多进…

力扣【347. 前 K 个高频元素】Java题解(堆)

TopK问题&#xff0c;我们直接上堆。 首先遍历一次然后把各个数字的出现频率存放在哈希表中便于后面堆的操作。 因为是出现频率前 k 高&#xff0c;所以用小顶堆&#xff0c;当我们遍历的频率值大于堆顶值时就可以替换堆顶。 代码&#xff1a; class Solution {public int[] …

[NOIP2007]矩阵取数游戏

点我写题 题目描述 帅帅经常跟同学玩一个矩阵取数游戏&#xff1a;对于一个给定的n*m的矩阵&#xff0c;矩阵中的每个元素aij均为非负整数。游戏规则如下&#xff1a; 1.每次取数时须从每行各取走一个元素&#xff0c;共n个。m次后取完矩阵所有元素&#xff1b; 2.每次取走的…

解决CentOS9系统下Zabbix 7.2图形中文字符乱码问题

操作系统&#xff1a;CentOS 9 Zabbix版本&#xff1a;Zabbix7.2 问题描述&#xff1a;主机图形中文字符乱码 解决方案&#xff1a; # 安装字体配置和中文语言包 sudo yum install -y fontconfig langpacks-zh_CN.noarch # 检查是否已有中文字体&#xff1a; fc-list :lan…

[SUCTF 2018]MultiSQL1

进去题目页面如下 发现可能注入点只有登录和注册,那么我们先注册一个用户&#xff0c;发现跳转到了/user/user.php&#xff0c; 查看用户信息,发现有传参/user/user.php?id1 用?id1 and 11,和?id1 and 12,判断为数字型注入 原本以为是简单的数字型注入&#xff0c;看到大…

计算机视觉-卷积

卷积-图像去噪 一、图像 二进制 灰度 彩色 1.1二进制图像 0 1 一个点可以用一个bit&#xff08;0/1&#xff09;来表示 1.2灰度图像 0-255 一个点可以用一个byte来表示 1.3彩色图像 RGB 表达一个彩色图像先说它的分辨率p/w&#xff08;宽&#xff09;和q/h&#xff08;高…

mybatis(78/134)

前天学了很多&#xff0c;关于java的反射机制&#xff0c;其实跳过了new对象&#xff0c;然后底层生成了字节码&#xff0c;创建了对应的编码。手搓了一遍源码&#xff0c;还是比较复杂的。 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE …

Vuex 的核心概念:State, Mutations, Actions, Getters

Vuex 的核心概念&#xff1a;State, Mutations, Actions, Getters Vuex 是 Vue.js 的官方状态管理库&#xff0c;提供了集中式的状态管理机制。它的核心概念包括 State&#xff08;状态&#xff09;、Mutations&#xff08;变更&#xff09;、Actions&#xff08;动作&#xf…

1.23 补题 寒假训练营

E 一起走很长的路&#xff01; 输入描述 第一行输入两个整数 n,q&#xff08;1≤n,q≤210^5&#xff09;&#xff0c;代表多米诺骨牌的个数和询问次数。 第二行输入 n 个整数 a1,a2,…,an​&#xff08;1≤ai≤10^9&#xff09;&#xff0c;表示多米诺骨牌的重量。 此后输入…

自定义数据集使用框架的线性回归方法对其进行拟合

代码 import torch import numpy as np import torch.nn as nncriterion nn.MSELoss()data np.array([[-0.5, 7.7],[1.8, 98.5],[0.9, 57.8],[0.4, 39.2],[-1.4, -15.7],[-1.4, -37.3],[-1.8, -49.1],[1.5, 75.6],[0.4, 34.0],[0.8, 62.3]])x_data data[:, 0] y_data data…

数据库-MySQL-事务-事务隔离级别的可重复读是如何实现的?

MySQL 事务隔离级别中的“可重复读”&#xff08;Repeatable Read&#xff09;是如何实现的 在MySQL中&#xff0c;可重复读&#xff08;Repeatable Read&#xff09;是默认的事务隔离级别&#xff0c;特别是在使用InnoDB存储引擎时。这个隔离级别通过多版本并发控制&#xff…

DDD架构实战第七讲总结:分层模型和代码组织

云架构师系列课程之DDD架构实战第七讲总结:分层模型和代码组织 一、引言 在前几讲中,我们介绍了领域驱动设计(DDD)的基本构造块和生命周期模型中的聚合。本讲将重点讨论如何将这些构造块和代码组织起来,探讨分层架构和六边形模型,以及如何组织代码结构。 二、工厂和资…

【中间件快速入门】什么是Redis

现在后端开发会用到各种中间件&#xff0c;一不留神项目可能在哪天就要用到一个我们之前可能听过但是从来没接触过的中间件&#xff0c;这个时候对于开发人员来说&#xff0c;如果你不知道这个中间件的设计逻辑和使用方法&#xff0c;那在后面的开发和维护工作中可能就会比较吃…

使用Python和Qt6创建GUI应用程序--前言

如果想用Python开发GUI应用程序&#xff0c;要从知道哪里是难点和棘手开始。有很多新的概念需要了解和学习&#xff0c;以获得和学习到可以展开工作所需要的东西。但是&#xff0c;像以往在编码所遇到的问题一样&#xff0c;第一步是学习如何处理以正确的方式解决问题。在这本书…

金晟新能源由盈转亏:毛利率下滑产能利用率不佳,关联交易持续增加

《港湾商业观察》黄懿 近期&#xff0c;广东金晟新能源股份有限公司&#xff08;下称“金晟新能源”&#xff09;递交了招股书&#xff0c;拟冲刺港交所IPO&#xff0c;中金公司、招银国际为联席保荐人。 金晟新能源处于电池回收的新兴大势行业&#xff0c;但是&#xff0c;受…

RTMP|RTSP播放器只解码视频关键帧功能探讨

技术背景 我们在做RTMP|RTSP直播播放器的时候&#xff0c;遇到过这样的技术诉求&#xff0c;在一些特定的应用场景中&#xff0c;可能只需要关键帧的信息&#xff0c;例如视频内容分析系统&#xff0c;可能只对关键帧进行分析&#xff0c;以提取特征、检测对象或场景变化。鉴于…

Spring Boot 集成 WebClient 实战教程 实现同步、异步请求处理以及响应式编程、响应式流、响应式Mono

该项目介绍springboot集成WebClient 实现服务的请求操作 示例中演示了,如何配置WebClient的请求头,请求参数等相关参数,实现同步、异步请求处理以及响应式编程、响应式流、响应式Mono。 为什么使用WebClient 不用RestTemplate 在 Spring Framework 5.0 及更高版本中,Res…

统计学中的样本概率论中的样本

不知道当初谁想的把概率论和数理统计合并&#xff0c;作为一门课。这本身是可以合并&#xff0c;完整的一条线&#xff0c;看这里。但是&#xff0c;作为任课老师应该从整体上交代清楚&#xff0c;毕竟是两个学科&#xff0c;不同的学科合并必然会有各种不协调的问题。 举个最…

2K高刷电竞显示器怎么选?

2K高刷电竞显示器怎么选&#xff1f;哪个价格适合你&#xff1f;哪个配置适合你呢&#xff1f; 1.HKC G27H2Pro - 2K高刷电竞显示器怎么选 外观设计 - HKC G27H2Pro 2K高刷电竞显示器 电竞风拉满&#xff1a;作为猎鹰系列的一员&#xff0c;背部 “鹰翼图腾” 切割线搭配炎红…