【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语言中的函数指针和排序算法。