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

2024-05-24 03:36

本文主要是介绍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函数,如有错误,请批评指正,感谢支持

这篇关于C语言指针相关知识(第四篇章)(非常详细版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/997259

相关文章

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

Ubuntu中远程连接Mysql数据库的详细图文教程

《Ubuntu中远程连接Mysql数据库的详细图文教程》Ubuntu是一个以桌面应用为主的Linux发行版操作系统,这篇文章主要为大家详细介绍了Ubuntu中远程连接Mysql数据库的详细图文教程,有... 目录1、版本2、检查有没有mysql2.1 查询是否安装了Mysql包2.2 查看Mysql版本2.

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

Win11安装PostgreSQL数据库的两种方式详细步骤

《Win11安装PostgreSQL数据库的两种方式详细步骤》PostgreSQL是备受业界青睐的关系型数据库,尤其是在地理空间和移动领域,:本文主要介绍Win11安装PostgreSQL数据库的... 目录一、exe文件安装 (推荐)下载安装包1. 选择操作系统2. 跳转到EDB(PostgreSQL 的

Python3.6连接MySQL的详细步骤

《Python3.6连接MySQL的详细步骤》在现代Web开发和数据处理中,Python与数据库的交互是必不可少的一部分,MySQL作为最流行的开源关系型数据库管理系统之一,与Python的结合可以实... 目录环境准备安装python 3.6安装mysql安装pymysql库连接到MySQL建立连接执行S

将Mybatis升级为Mybatis-Plus的详细过程

《将Mybatis升级为Mybatis-Plus的详细过程》本文详细介绍了在若依管理系统(v3.8.8)中将MyBatis升级为MyBatis-Plus的过程,旨在提升开发效率,通过本文,开发者可实现... 目录说明流程增加依赖修改配置文件注释掉MyBATisConfig里面的Bean代码生成使用IDEA生

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1