个人主页(找往期文章包括但不限于本期文章中不懂的知识点): 我要学编程(ಥ_ಥ)-CSDN博客
目录
回调函数
qsort使用举例
使用qsort函数排序整型数据
使用qsort排序结构数据
qsort函数的模拟实现
回调函数
回调函数就是一个通过函数指针调用的函数。 如果你把函数指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
简单来说就是:当我拥有一个函数(地址)指针时,在一些特定的情况下,通过函数指针回过头来调用这个函数,那么这个被调用的函数就是回调函数。
在简易计算器的制作(函数指针数组的实践)-CSDN博客这篇文章中写的计算代码,就是可以用这个回调函数的例子。如果我们要使用加法计算两个数的值,不是直接由main函调用的,而是由calc函数来调用对应的加法函数。这便是回调函数。
qsort使用举例
使用qsort函数排序整型数据
如果我们想要排序一组数据,按照升序或者降序的方式,首先想到的就是冒泡排序。这个排序在深入解剖指针篇(2)-CSDN博客 这篇文章中介绍了。核心思想就是:两两相邻元素的比较。
我们现在就用qsort函数来实现。首先,就得了解什么是qsort函数。
上面就是关于qsort函数的基本介绍。
void qsort(void* base, //这个base就是要排序的元素的起始地址
size_t num, //这个num就是要排序的元素个数
size_t size,//这个size就是要排序的元素对应字节数
int (*compar)(const void*, const void*));//这个其实就是一个 有比较功能函数 的指针
上面那个比较功能就是你要怎么实现这个排序,就用什么功能。例如:我要实现升序功能,那么这个函数就是实现升序的功能。
我们现在就可以开始模拟实现冒泡排序了。
#include <stdio.h>
#include <stdlib.h>//qsort函数所需的头文件
//打印数据看看是否排序成功
void Print(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//调用的排序函数
//由于创造这个函数的人,不知道我们会比较什么样的数据,因此,就要void*来接收。
int int_cmp(const void* p1, const void* p2)
{
//qsort默认的是升序,由于void*不能直接解引用,所以就先得强制转换为要比较(int*)的数据类型
return (*(int*)p1 - *(int*)p2);//如果我们像排成降序,就可以把这个给反过来
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), int_cmp);
Print(arr, sz);
return 0;
}
可以看出来这个qsort函数排序是正确的。
使用qsort排序结构数据
我们假设要排序一些不是整型的数据,那么冒泡排序肯定是不行的。但是我们可以采用其思想,来进行排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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);
}
void Print(struct stu* p, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s ", (p+i)->name);//结构体指针->成员名
}
printf("\n");
}
int main()
{
//创建一个结构体数组并且初始化
struct stu arr[] = { {"zhangsan", 20},{"lisi" ,30}, {"wangwu", 18} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
Print(arr, sz);
return 0;
}
strcmp是用来比较字符串大小的,具体用法请看:字符函数与字符串函数(上)-CSDN博客
通过上面的学习,我们就可以发现这个qsort函数在使用时,需要我们根据自己的需求来写出对应的比较函数。
结构体成员的间接访问
我们在访问结构体成员时,有两种操作符,一种是 . (直接访问),还有一种是 ->(间接访问)。
直接访问在操作符详解(下)-CSDN博客 里讲过。
结构体指针->成员名 ,即当 -> 的左边满足时结构体指针时,我们要访问结构体成员就可以用->。
上面是排序其名字,我们还可以排序其年龄
#include <stdio.h>
#include <stdlib.h>
#include <string.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);
}
void Print(struct stu* p, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", (p + i)->age);
}
printf("\n");
}
int main()
{
struct stu arr[] = { {"zhangsan", 20},{"lisi" ,30}, {"wangwu", 18} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
Print(arr, sz);
return 0;
}
qsort函数的模拟实现
我们现在就来通过模拟实现qsort函数(用冒泡的方式)来排序整形数组。
#include <stdio.h>
int cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
//因为前面有强制转换为char*了,所以我们也就不用void*了
void Swap(char* p1, char* p2, size_t width)
{
//如果是排序char的数据,我们就只能一个字节一个字节的交换
for (int i = 0; i < width; i++)
{
char tmp = *(p1+i);
*(p1+i) = *(p2+i);
*(p2+i) = tmp;
}
}
void bubble_sort(void* base, size_t num, size_t width, int(*cmp)(void*, void*))
{
for (int i = 0; i < num - 1; i++)
{
for (int j = 0; j < num - 1 - i; j++)
{
//如果前者大于后者,也就是降序,会返回一个大于0的数(根据这个cmp的函数来的)
//不知道是啥类型,强制转换为最小的就行(类型占字节最小的是字符型)
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp);//采用冒泡的方式
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}