qsort函数
- 前言
- qsort函数
- (一)引例:简单的冒泡排序
- (二)qsort函数接收
- 1.介绍
- 2.void*
- (三)使用
- 1.用qsort实现一个比较整型的函数
- 2.用qsort实现一个比较结构体的函数
- 3.用qsort实现一个比较浮点型的函数
- (四)用冒泡排序模拟qsort函数的使用
- 1.模拟qsort实现一个比较整数的函数
- 2.模拟qsort实现一个比较结构体的函数
- 3.模拟qsort实现一个比较浮点型的函数
- 总结
前言
在学习回调函数的时候,不免会觉得难以理解或者难以运用,那就需要引入一个例子qsort库函数来让大家理解理解真正的回调函数是怎么样的,这就需要我们进行深入的了解什么是qsort函数,我们是先根据MSDN去查了查什么是qsort,然后就依据它的本质进行演示了不同类型的数的排序,最后模拟了一下冒泡排序运用qsort函数的使用,这都是干货满满!!!
qsort函数
要了解qsort函数,那就需要先引进一个简单的冒泡函数:
(一)引例:简单的冒泡排序
#include<stdio.h>
//缺陷:只能排序函数
bubble_sort(int* arr, int sz) {
int i = 0;
for (i = 0; i < sz - 1; i++) {
int j = 0;
for (j = 0; j < sz - 1 - i; j++) {
if (*(arr + j) > *(arr + j + 1)) {
int temp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = temp;
}
}
}
}
int main() {
//对数组进行排序,升序
int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
//打印
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
似乎有点缺陷啊,那我们要排字符呢?排结构体呢?是不是没法用大于号小于号和等于号进行比较了,那我们是不是需要把这个比较的式子单独抽离出来,在单独空间进行比较。
(二)qsort函数接收
1.介绍
2.void*
如果我们用整型指针去接收浮点型指针的时候,出现不兼容的情况,所以qsort的作者也考虑到了这一情况,既然不兼容,那我们找一个典当行,我用什么类型的指针传递给他(什么废铜烂铁都给他),他肯定看看不报错,因为总是有值钱的嘛,那这个典当行就是void*即空类型的指针。
所以我们为什么要用void*来接收传参呢?
我们发现float*到int*是不匹配的,那我们就需要void*了。
#include<stdio.h>
int main() {
int a = 10;
float f = 5.5f;
int* p = &a;
//p = &f;
//好处:
//pp为通用的指针,什么地址都可以放到pp指针里面
//void*自己都不知道自己是什么类型的指针
void* pp = &f;
pp = &a;
//弊端:
//void*指针不能直接用,不能直接进行运算和打印
pp++;//err void*未知的大小
printf("%f\n", *pp);//err 非法的间接寻址
return 0;
}
这个典当行行长void*说你们可以把不同的指针类型丢给我但是我也有条件的,你给我的什么破铜烂铁啊!我不输出了,你让我加价!?那肯定不可能的,可是我们想要达到我们的目的怎么办呢?我们就需要用暴力手段了,强制类型转换,跟典当行老板说,你今天必须给我加价并报出我满意的价格,不然告你黑店,典当行行长说,好好好,少侠饶命,我肯定加价报出好价格,这下子只要进行强制类型转换就好了。
如下图进行:
(三)使用
1.用qsort实现一个比较整型的函数
思路是利用qsort函数进行比较函数传参,即cmp_int函数,这个函数就是我们的回调函数,因为它进行传递地址给asort函数,qsort函数接收了cmp_int函数的地址并进行回调,回调以后接受比较的两个元素进行强制类型转换为整型指针进行相加减,根据qsort函数的返回值进行排序升序还是降序。
代码如下:
#include<stdio.h>
#include<stdlib.h>
//实现一个比较整型的函数
int cmp_int(const void* elem1, const void* elem2) {
return *(int*)elem1 - *(int*)elem2;//升序
//return *(int*)elem2 - *(int*)elem1;//降序
}
//使用qsort函数对数组进行排序,升序
void test1(void) {
//对数组进行排序,升序
int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
//bubble_sort(arr, sz);
//库函数中有一个专门排序的函数叫qsort
qsort(arr, sz, sizeof(arr[0]), &cmp_int);//&cmp_int是取出函数的地址并进行回调
//打印
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
}
int main() {
test1();
//test2();
return 0;
}
2.用qsort实现一个比较结构体的函数
思路是跟上面相似,而结构体这边要用的是要定义一个结构体并进行定义结构体数组,这里用到的技巧是结构体指针用的是->,还有一个比较强的技巧是strcmp,这里的strcmp库函数的返回值与qsort函数的返回值完全一致,我们打开MSDN看看:
结构体内元素类型不同不能进行比较哦,只能进行比较同类型的元素。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//使用qsort函数排序结构体
struct Stu {
char name[30];
int age;
};
//先按照年龄排序
int cmp_stu_by_age(const void* elem1, const void* elem2) {
return ((struct Stu*)elem1)->age - ((struct Stu*)elem2)->age;
}
//再按照名字排序
int cmp_stu_by_name(const void* elem1, const void* elem2) {
return strcmp(((struct Stu*)elem1)->name, ((struct Stu*)elem2)->name);
}
void test2(void) {
struct Stu s[3] = { {"zhangsan",20 },{"lisi",32},{"wangwu",55} };
int sz = sizeof(s) / sizeof(s[0]);
//qsort(s, sz, sizeof(s[0]), &cmp_stu_by_age);
qsort(s, sz, sizeof(s[0]), &cmp_stu_by_name);
int i = 0;
for (i = 0; i < sz; i++) {
printf("%s %d\n", s[i].name, s[i].age);
}
}
int main() {
//test1();
test2();
return 0;
}
3.用qsort实现一个比较浮点型的函数
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp_float(const void* elem1, const void* elem2) {
if (*(float*)elem1 > *(float*)elem2){
return 1;
}
else if (*(float*)elem1 < *(float*)elem2){
return -1;
}
else {
return 0;
}
}
void test3(void) {
//对数组进行排序,升序
float arr[] = { 0.3, 0.4, 0.1, 0.2, 0.5 };
int sz = sizeof(arr) / sizeof(arr[0]);
//bubble_sort(arr, sz);
//库函数中有一个专门排序的函数叫qsort
qsort(arr, sz, sizeof(arr[0]), &cmp_float);
//打印
int i = 0;
for (i = 0; i < sz; i++) {
printf("%.1f ", arr[i]);
}
}
int main() {
//test1();
//test2();
test3();
return 0;
}
(四)用冒泡排序模拟qsort函数的使用
1.模拟qsort实现一个比较整数的函数
先上代码再分析:
#include<stdio.h>
//改造冒泡排序函数,使用这个函数可以排序任意指定的数组
int cmp_int(const void* elem1, const void* elem2) {
return *(int*)elem1 - *(int*)elem2;//升序
//return *(int*)elem2 - *(int*)elem2;//降序
}
void Swap(char* buf1, char* buf2, int width) {
int i = 0;
for (i = 0; i < width; i++) {
int temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
//base就是未知参数的起始地址,
//sz就是传过来的数组大小,
//width是传过来数组内一个元素的字节长度
//int (*cmp)(const void*elem1, const void*elem2)是比较方法
//void*的意思就是不知道要传的是什么类型
bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void*elem1, const void*elem2)) {
int flag = 1;
size_t i = 0;
for (i = 0; i < sz - 1; i++) {
size_t 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);
flag = 0;
}
}
if (flag) {
break;
}
}
}
//使用自己写的bubble_sort排序整型数组
void test3(void) {
//对数组进行排序,升序
int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), &cmp_int);
//打印
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
}
int main() {
//test3();
test4();
return 0;
}
大家可以点开图片一点一点看,这个图片有点大。
细节:
2.模拟qsort实现一个比较结构体的函数
先上代码再分析:
#include<stdio.h>
//使用自己写的bubble_sort排序结构体数组
struct Stu {
char name[30];
int age;
};
//先按照年龄排序
int cmp_stu_by_age(const void* elem1, const void* elem2) {
return ((struct Stu*)elem1)->age - ((struct Stu*)elem2)->age;
}
//再按照名字排序
int cmp_stu_by_name(const void* elem1, const void* elem2) {
return strcmp(((struct Stu*)elem1)->name, ((struct Stu*)elem2)->name);
}
void test4(void) {
struct Stu s[3] = { {"zhangsan",20 },{"lisi",32},{"wangwu",55} };
int sz = sizeof(s) / sizeof(s[0]);
bubble_sort(s, sz, sizeof(s[0]), &cmp_stu_by_age);
//bubble_sort(s, sz, sizeof(s[0]), &cmp_stu_by_name);
int i = 0;
for (i = 0; i < sz; i++) {
printf("%s %d\n", s[i].name, s[i].age);
}
}
int main() {
//test3();
test4();
return 0;
}
比较结构体就和前面的qsort函数一样了,只不过换了个名字,因为要用到的都是地址。
3.模拟qsort实现一个比较浮点型的函数
比较浮点型的就需要考虑考虑了,那为了方便起见我们还是比较大小吧,如果相加减精度超过了范围那就不好办了,所以还是直接判断大小更加稳妥一点。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//使用自己写的bubble_sort排序浮点型数组
int cmp_float(const void* elem1, const void* elem2) {
if (*(float*)elem1 > *(float*)elem2) {
return 1;
}
else if (*(float*)elem1 < *(float*)elem2) {
return -1;
}
else {
return 0;
}
}
void Swap(char* buf1, char* buf2, int width) {
size_t i = 0;
for (i = 0; i < width; i++) {
int temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* elem1, const void* elem2)) {
int flag = 0;
size_t i = 0;
for (i = 0; i < sz - 1; i++) {
size_t 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);
flag = 1;
}
}
if (flag) {
break;
}
}
}
void test5(void) {
//对数组进行排序,升序
float arr[] = { 0.1,0.3,0.2,0.4,0.5 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), &cmp_float);
//打印
int i = 0;
for (i = 0; i < sz; i++) {
printf("%.1f ", arr[i]);
}
}
int main() {
//test3();
//test4();
test5();
return 0;
}
总结
总的来讲,qsort函数是很有趣的,当我们一步步去探索,一步步去剖析,会发现很多有趣的库函数,这篇博客细致讲解了qsort函数的使用,先是很细致的讲解了qsort函数的介绍以及使用,再是以一个冒泡排序的思想模拟实现了一个比较不同类型的函数,可谓是干货满满!
客官,码字不易,来个三连支持一下吧!!