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

server/2025/1/31 9:41:10/

深入理解指针(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.ppmy.cn/server/163772.html

相关文章

xss总结标签

前言&#xff1a; 在网上找标签费时间&#xff0c;而且有时候忘记怎么找了&#xff0c;所以现在写一篇 由以下大佬赞助发布 xss 常用标签及绕过姿势总结 - FreeBuf网络安全行业门户 内容&#xff1a; <a>标签 <a href"javascript:alert(1)">test&l…

通义灵码插件保姆级教学-IDEA(安装及使用)

一、JetBrains IDEA 中安装指南 官方下载指南&#xff1a;通义灵码安装教程-阿里云 步骤 1&#xff1a;准备工作 操作系统&#xff1a;Windows 7 及以上、macOS、Linux&#xff1b; 下载并安装兼容的 JetBrains IDEs 2020.3 及以上版本&#xff0c;通义灵码与以下 IDE 兼容&…

深度研究新范式:通过Ollama和DeepSeek R1实现自动化研究

引言 在信息时代&#xff0c;海量数据的产生与传播速度前所未有地加快&#xff0c;这既为研究者提供了丰富的资源&#xff0c;也带来了信息筛选与处理的巨大挑战。 传统研究方法往往依赖于研究者的个人知识库、文献检索技能以及时间投入&#xff0c;但面对指数级增长的数据量…

K8S中ingress详解

Ingress介绍 Kubernetes 集群中&#xff0c;服务&#xff08;Service&#xff09;是一种抽象&#xff0c;它定义了一种访问 Pod 的方式&#xff0c;无论这些 Pod 如何变化&#xff0c;服务都保持不变。服务可以被映射到一个静态的 IP 地址&#xff08;ClusterIP&#xff09;、一…

IDEA常用快捷键

目录 ctrlaltt \quad \quad \quad \quad 按住ctrlalt 再点击就可以进入 \quad \quad \quad \quad

【ESP32】ESP-IDF开发 | WiFi开发 | TCP传输控制协议 + TCP服务器和客户端例程

1. 简介 TCP&#xff08;Transmission Control Protocol&#xff09;&#xff0c;全称传输控制协议。它的特点有以下几点&#xff1a;面向连接&#xff0c;每一个TCP连接只能是点对点的&#xff08;一对一&#xff09;&#xff1b;提供可靠交付服务&#xff1b;提供全双工通信&…

Linux实操篇-文件目录类>/>>/echo/head/tail/ln/history

目录 传送门前言一、>、 >>概念二、>、 >>实战1. **>&#xff08;输出重定向&#xff09;**2. **>>&#xff08;追加输出&#xff09;****区别总结&#xff1a;** 三、echo、head、tail概念四、echo、head、tail实战1. **echo****用法**&#xff1a…

【玩转全栈】----Django模板的继承

先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01; 目录 模板继承的好处 模板继承的语法规则 更新代码 上文中的部门管理页面&#xff1a; 【玩转全栈】----Django制作部门管理页面-CSDN博客 大家会发现&#xff0c;由于定义了多个html文件&#xff0c;多个ht…