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

相关文章

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

Go语言使用Gin处理路由参数和查询参数

《Go语言使用Gin处理路由参数和查询参数》在WebAPI开发中,处理路由参数(PathParameter)和查询参数(QueryParameter)是非常常见的需求,下面我们就来看看Go语言... 目录一、路由参数 vs 查询参数二、Gin 获取路由参数和查询参数三、示例代码四、运行与测试1. 测试编程路