C语言指针相关知识(第四篇章)(非常详细版)

文章目录

  • 前言
  • 一、什么是回调函数
  • 二、qsort函数的介绍(默认升序排序)
  • 三、qsort函数的模拟实现(通过冒泡排序)
  • 总结


前言

本文介绍了回调函数,qsort函数的使用,以用冒泡排序来模拟实现qsort函数


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是回调函数

  • 前面的博客里面我介绍了函数指针变量的相关概念,而回调函数就是通过一个函数指针调用的函数。进一步说,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数,注意哈,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生的时候由另一方调用哒,用于对该事件或条件进行响应。
  • 我们使用回调函数其实可以简化代码,省去一些冗余重复的操作
    以下是实现一个计算器的代码,我们在没有使用回调函数之前:
#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 x, y;
    int input = 1;
    int ret = 0;
    do
    {
    //菜单:
printf("*************************\n");
        printf("  1:add        2:sub\n");
        printf("  3:mul        4:div\n");
        
printf("*************************\n");
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        //这之后的代码就较为冗余,重复之处比较多
        case 1:
            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");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
 }

在实现计算器的过程中,我们可以发现在case语句中输入输出较为冗余,重复次数过多,这里我们就可以用回调函数来简化代码,设置一个操作函数,参数为函数指针变量来简化代码。具体操作如下:

#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;
 }
void calc(int(*pf)(int,int))
{
int ret = 0;
int x,y;
printf("输入操作数:");
scanf("%d %d",&x,&y);
ret = pf(x,y);//这里我们通过函数指针调用相关函数,所被调用的函数即为回调函数。
printf("ret=%d\n",ret);
}
int main()
 {
    int x, y;
    int input = 1;
    int ret = 0;
    do
    {
    //菜单:
printf("*************************\n");
        printf("  1:add        2:sub\n");
        printf("  3:mul        4:div\n");
        
printf("*************************\n");
        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;
 }

很明显我重新定义了一个函数calc,而其参数为函数指针变量,我们将我们要执行的加减乘除函数传递到函数中,就可以减少重复的输入和输出代码,从而做到了简化代码的功效。

二、qsort函数的介绍(默认升序排序)

  • qsort函数是我们C语言库中用来专门用来排序的库函数(头文件为:stdlib.h)
  • 定义声明为:
  • void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
  • base代表待排序序列,num,为序列中元素个数,size,代表每个元素所代表的字节大小,最后一个参数为函数指针类型,是一个比较函数,用来阐述比较规则的。
  • 对于最后一个参数函数指针类型的参数,我们通过它的返回值来确定具体那个元素在前,那个元素在后:
    若返回值<0,则第一个指针指向的元素在前,第二个指针指向的元素在后;若返回值=0,则默认第一个指针指向的元素在前,第二个指向的元素在后;若返回值>0,则第一个指针指向的元素在后,第二个指针指向的元素在前。
  • 使用qsort函数来排序整型数据:
    代码显示:
#include<stdio.h>
#include<stdlib.h>
int int_cmp(const void *p1,const void*p2)
//实现泛式编程,我们定义void*指针,这样就可以接受任何类型的数据。后面只需要强制类型转换成我们所需要的数据类型即可。
{
return (*(int*)p1-*(int *)p2);
}
int main()
{
int arr[]={1,3,5,7,9,2,4,6,8,0};
int i =0;
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(int),int_cmp);//根据需要传递相应的参数。
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d ",arr[i] );
}
printf("\n");
}
  • 使用qsort函数排序结构数据
  • 我们来进行结构体的排序以学生结构体为例,我们进行分别以学生的名字为依据和学生的年龄为依据进行比较。
    以年龄为依据进行排序:
#include<stdio.h>
#include<stdlib.h>
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 main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//这里我们定义结构体序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;

这里的排序结果我们通过调试来显示:
排序前:
在这里插入图片描述
排序后:
在这里插入图片描述
以名字为依据进行排序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>//我们需要调用字符串函数strcmp函数进行字符串的比较
struct Stu //定义学生结构体变量
{
char name[20];//学生名字
int age;//年龄
}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,35"}};//这里我们定义结构体序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;

我们通过调试来显示排序结果:
排序前:

在这里插入图片描述
排序后:
在这里插入图片描述
这里strcmp字符串比较函数的返回值正好符合我们qsort对于比较函数返回值的要求,二者可谓是不谋而合呀。

三、qsort函数的模拟实现(通过冒泡排序)

  • 我在之前的博客里面已经实现过我们所熟悉的冒泡排序代码算法:
#include<stdio.h>
void input(int* arr, int sz)//输入待排序序列
{
	for (int i = 0; i < sz; i++)
	{
		scanf("%d", arr + i);
	}
}

void bubble_sort(int* arr, int sz)//冒泡排序算法
{
	for (int i = 0; i < sz-1; i++)//sz-1趟比较
	{
		int change = 1;//小优化节省时间
		for (int 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;
				change = 0;
			}
		}
		if (change == 1)//说明已经有序 
		{
			break;
		}
	}
}
void print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
	printf("\n");
}
int main()
{
	int arr[10] = { 0};
	int sz = sizeof(arr) /sizeof(arr[0]);
	input(arr, sz);
	bubble_sort(arr, sz);
	print(arr, sz);
	return 0;
}

但是在这里我们为了响应qsort算法,我们应该根据qort函数中的参数来重新改编冒泡排序。
前面已经提到过qsort函数的函数声明:
void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
这里我们用void指针来接受待排序序列,是一种泛式的编程,这里就可以接受任何数据类型的排序,这便是void指针的最大优势。

我们用冒泡排序模拟实现qsort函数的代码如下(这里我们以排序整型数据为例):

#include<stdio.h>
int int_cmp(const void*p1,const void * p2)//定义比较函数
{ 
 return (*( int *)p1 - *(int *) p2);
}
void _swap(void*p1,void*p2,int size)
{
for(int i=0;i<size;i++)
{
//每一位字节都进行交换,从而做到整个数据类型进行交换。
 char tmp = *((char *)p1 + i);//我们转换成char*类型可以理解为转换成单位字节,然后乘上数据类型字节大小就可以表示任意数据类型
 *(( char *)p1 + i) = *((char *) p2 + i);
 *(( char *)p2 + i) = tmp;
}


}
void bubble(void*base,int count ,int size,int(*cmp)(void*,void*))
//这里完全模仿qsort函数来定义的
{
for(int i=0;i<count-1;i++)
{
for(int j=0;j<count-1-i)
{
if(cmp((char*)base+j*size,(char*)base+(j+1)*size)>0)//我们转换成char*类型可以理解为转换成单位字节,然后乘上数据类型字节大小就可以表示任意数据类型
{
 _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
int main()
 {
 int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
 int i = 0;
 bubble(arr, sizeof(arr) /sizeof(arr[0]), sizeof (int), int_cmp);
 for (i = 0;i<sizeof(arr)/sizeof(arr[0]); i++)
 {
 printf( "%d ", arr[i]);
 }
 printf("\n");
 return 0;
 }

总结

本文主要介绍了一个崭新的概念回调函数,并分析了qsort函数的使用,以及用冒泡排序来模拟qsort函数,如有错误,请批评指正,感谢支持

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

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

相关文章

ASP+ACCESS基于B2C电子商务网站设计

摘 要 运用ASP技术结合了Access数据库原理&#xff0c;基于B/S模式我们开发了一个网上购物系统。在我们的系统中&#xff0c;顾客可以很方便的注册成为会员&#xff0c;对商品进行浏览检索&#xff0c;查看商品的详细资料&#xff0c;然后根据各人的喜好购买心仪的商品。系统…

Day21:Leetcode513.找树左下角的值 +112. 路径总和 113.路径总和ii + 106.从中序与后序遍历序列构造二叉树

LeetCode&#xff1a;513.找树左下角的值 解决方案&#xff1a; 1.思路 在遍历一个节点时&#xff0c;需要先把它的非空右子节点放入队列&#xff0c;然后再把它的非空左子节点放入队列&#xff0c;这样才能保证从右到左遍历每一层的节点。广度优先搜索所遍历的最后一个节点…

Amesim基础篇-元件详解-流体库管路

1 流体类型选择 如下图1所示&#xff0c;流体可以选择流体库中自带的流体。常用的流体类型为50%乙二醇溶液&#xff08;EG50W50 coolant&#xff09;。 2 管路 该模型是常用的管路&#xff0c;如下图所示。如之前所讲的建模原则&#xff1a;严格遵循R-C-R-C的原则。下图中的管…

Golang | Leetcode Golang题解之第98题验证二叉搜索树

题目&#xff1a; 题解&#xff1a; func isValidBST(root *TreeNode) bool {stack : []*TreeNode{}inorder : math.MinInt64for len(stack) > 0 || root ! nil {for root ! nil {stack append(stack, root)root root.Left}root stack[len(stack)-1]stack stack[:len(s…

领域知识 | 智能驾驶安全领域部分常见概论

Hi&#xff0c;早。 最近想买个新能源车&#xff0c;这个车吧相比于之前的内燃车&#xff0c;新能源车与外界的交互多了很多。比如娱乐的第三方应用&#xff0c;OTA升级等应用。 交互带来的便利越多&#xff0c;暴露的风险自然也就越大&#xff0c;相比于手机等消费者终端设备…

Linux信号:信号的保存

目录 一、信号在内核中的表示 二、sigset_t 2.1sigset_t的概念和意义 2.2信号集操作数 三、信号集操作数的使用 3.1sigprocmask 3.2sigpending 3.3sigemptyset 四、代码演示 一、信号在内核中的表示 实际执行信号的处理动作称为信号 递达(Delivery) 。 信号从产生到递达…

如何使用WindowsSpyBlocker防止Windows系统被恶意监控和跟踪

关于WindowsSpyBlocker WindowsSpyBlocker是一款功能强大的Windows系统安全防护工具&#xff0c;该工具基于Go语言开发&#xff0c;WindowsSpyBlocker以一个单独的可执行程序发布&#xff0c;可以帮助广大用户防止自己的Windows系统被恶意监控和跟踪。 WindowsSpyBlocker能够利…

【手势识别-UIPinchGestureRecognizer捏合-UIPanGestureRecognizer缩放 Objective-C语言】

一、接下来,我们来说这个捏合,和,这个缩放啊 1.捏合, 首先呢,步骤,也都是一样的啊, 1)创建手势对象 2)添加手势 3)实现手势方法 pinch:捏合 UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:(id) action:(SEL)]; U…

使用大模型结合Mermaid实现业务流程图快速生成

一、需求描述 在日常系统研发过程中&#xff0c;经常面临前期要写投标技术文档&#xff0c;中期要写系统概要设计、详细设计等各类文档&#xff0c;最耗时间的便是画一些业务流程图。随着大模型的不断普及&#xff0c;大模型对文字的处理越来越强&#xff0c;现可以找一个能简化…

MySQL主从复制+读写分离(ShardingJDBC)

MySQL主从复制读写分离 MySQL主从复制介绍二进制日志&#xff1a; MySQL的主从复制原理如下搭建主从复制准备工作主库配置从库配置 测试 读写分离案例ShardingJDBC介绍数据库环境初始工程导入读写分离配置测试1). 保存数据2). 修改数据3). 查询数据4). 删除数据 MySQL主从复制 …

LVS精益价值管理系统 DownLoad.aspx 任意文件读取漏洞复现

0x01 产品简介 LVS精益价值管理系统是杭州吉拉科技有限公司研发的一款专注于企业精益化管理和价值流优化的解决方案。该系统通过集成先进的数据分析工具、可视化的价值流映射技术和灵活的流程改善机制&#xff0c;帮助企业实现高效、低耗、高质量的生产和服务。 0x02 漏洞概述…

Java ( 框架界面 , 按钮 , 动作监听ActionListener ,鼠标监听MouseListener,键盘监听KeyListener)的使用方法

package 拼图阶段任务.ui;import javax.swing.*; import java.awt.*; import java.awt.event.*;public class UseMethod {public static void main(String[] args) { // 框架的用法JFrame jf new JFrame();// 设置界面的宽高jf.setSize(603,680);// 设置界面的标题jf.setTitle…

Redis的下载、安装、启动和初尝试【超级简单】

redis最好是在Linux系统中使用&#xff0c;这是最接近生产实际的环境。 不过&#xff0c;我们初学者&#xff0c;目的是学习Redis的使用、原理&#xff0c;如果在Linux下直接学习Redis&#xff0c;很可能会因为命令不熟悉而劝退&#xff0c;这是不好的。 因此&#xff0c;我主张…

国际货币基金组织警告:网络攻击影响全球金融稳定

近日&#xff0c;在一份关于金融稳定的报告中&#xff0c;国际货币基金组织&#xff08;IMF&#xff09;用了一章&#xff08;共三章&#xff09;的篇幅描述了网络攻击对金融环境的影响&#xff0c;并警告称&#xff0c;全球金融稳定正受到日益频繁和复杂的网络攻击的威胁。同时…

kubernetes(k8s) v1.30.1 创建本地镜像仓库 使用本地docker镜像仓库部署服务 Discuz X3.5 容器搭建论坛

1 master11创建本地镜像仓库 [rootmaster11 ~]# docker run -d -p 5000:5000 --restartalways --name registry registry:2 Unable to find image registry:2 locally 2: Pulling from library/registry 79e9f2f55bf5: Pull complete 0d96da54f60b: Pull complete 5b27040df…

是德科技 DSOS104A MSOS104A示波器

产品 带宽 通道数 最大存储器深度 DSOS104A 高清晰度示波器 1 GHz 4 个模拟通道 800 Mpts MSOS104A 高清晰度示波器 1 GHz 4 个模拟通道和 16 个数字通道 800 Mpts 商品介绍 …

ELK 日志监控平台(一)- 快速搭建

文章目录 ELK 日志监控平台&#xff08;一&#xff09;- 快速搭建1.ELK 简介2.Elasticsearch安装部署3.Logstash安装部署4.Kibana安装部署5.日志收集DEMO5.1.创建SpringBoot应用依赖导入日志配置文件 logback.xml启动类目录结构启动项目 5.2.创建Logstash配置文件5.3.重新启动L…

锁相环的一些学习笔记--(1)

下图两组1.2.3可以对应起来&#xff1b; 参考资料&#xff1a; 1. Matlab https://www.bilibili.com/video/BV1bR4y1Z7Xg/?spm_id_from333.1296.top_right_bar_window_history.content.click&vd_source5556680656536651c49f5e55d7d4df23 2. https://www.bilibili.com/…

Hadoop开发之JavaAPI操作HDFS

目录 一、maven的安装与配置1.maven的下载2.maven的安装与配置&#xff08;1&#xff09;解压&#xff08;2&#xff09;创建本地仓库文件夹&#xff08;3&#xff09;修改settings.xml配置文件&#xff08;4&#xff09;配置maven的环境变量&#xff08;5&#xff09;idea中ma…

将list对象里的某一个属性取出组成一个新的list

使用Java8将对象里的某一个属性取出组成一个新的list List<Spgg1> listnew ArrayList<>();Spgg1 spgg1new Spgg1();spgg1.setSpdm("测试");spgg1.setGgdm("001");list.add(spgg1);Spgg1 spgg2new Spgg1();spgg2.setSpdm("测试2");sp…