前言
排序是我们日常编写程序经常可以用到的,冒泡排序也是我们最常见的排序方法,在这里我们分析一下冒泡排序,以及引入我们今天的主角----->C语言库函数之快速排序的qsort函数
一、冒泡排序
首先我们先引入冒泡排序的代码:
#include <stdio.h>
int main()
{
int arr[10] = { 3,4,2,7,8,9,6,0,1,5 };
int i = 0;
int j = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (j = 1; j < sz; j++)
{
for (i = 0; i < sz - j; i++)
{
if (arr[i] > arr[i + 1])
{
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
几条重要的代码解析:
1.计算数组元素的个数
![]()
2.冒泡排序循环的趟数
![]()
就我们目前的代码,sz的数值为10,那么如果排序好9个数字,剩余的那1个数字肯定也是排好序的了,所以可以知道冒泡排序的趟数要比元素个数少1
3. 冒泡排序的元素个数

就我们目前的代码,sz的数值为10,每一趟冒泡排序都可以排序好一个数字,所以下次循环的元素个数要-1,把排序好的那个数字剔除掉,将剩余的元素进行下一次冒泡排序
4.比较判断

如上代码我们是将数组元素升序排列,所以判断是不是有前面的元素比后面的元素小的这种情况,如果有进入 if 语句,进行元素的交换
二、冒泡排序的局限性

我们上图编写的代码仅限于比较整型元素,如果我们把整型arr数组改成--->字符arr数组或者是结构体数组(因为比较的方法同整型的比较方法不一致),所以这样的数组在我们目前编辑好的冒泡排序中是无法进行排序的
所以我们可以得出结论我们日常编写的冒泡排序,不能对各种类型的变量进行排序
下面我们引入今天的主角qsort函数
三、qsort函数的使用及优点
1.qsort函数所需要的头文件
![]()
2.qsort函数所需要的参数以及返回类型
![]()
我们看到qsort函数的返回类型为void(空类型)
然后我们逐个看函数所需要的参数
a.
base为所需要排序的数组首元素地址
b.
需要排序的数组的元素个数
c.
数组元素的大小(单位:字节)
d.
这里我们需要自己写一个比较函数
在这里我们不知道数组的所有信息(类型,元素个数,元素大小),所以在这里我们对于元素个数和元素大小统一采用 size_t的类型(无符号数)对于数组首元素的地址,由于我们不知道所需要排序的数组是什么类型的,所以我们用void* 来接收数组首元素的地址(void* 可以转化为任意类指针类型),我们自己编写的比较函数,因为不知道元素类型,所以统一写为 void* 类型去接收两个变量的地址,在qsort函数中传入我们自己编写的比较函数的地址即可。
e.自己编写的cmp函数的返回值

所以返回值大于0升序排列,小于0降序排列,同我们冒泡排序中的if条件判断语句的那段代码,判断与后方元素的大小关系
比较函数的代码如下:
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
如上图所示我们比较是两个整型
因为e1是存放的变量类型的地址并且为void*类型所以我们需要把e1强制转化为我们所需的比较类型如上的代码比较的是整型数据,所以我们把e1强制转换为int*类型,最后再解引用得到元素,e2同理,最后返回的是两个元素相减后与0的大小情况
qsort函数的全部代码:
#include <stdio.h>
#include <stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 2,7,1,3,4,5,6,9,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
![]()
如上图的代码我们是将数组进行了升序的排列
如果我们需要改为降序的排列我们仅需要把e1,e2的位置交换即可
下面我们尝试一下利用qsort函数去对结构体进行排序
代码如下:(按名字排序)我们利用qsort函数只需要去变换比较的函数就可以实现对任意类型的比较 在这里的字符串比较我们不能用加减 我们需要借助库函数strcmp 其他的同整型的判断方法一致
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
char name[20];
int age;
};
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int main()
{
struct stu s[3] = { {"zhangsan",11},{"lisi",10},{"wangwu",12} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_name);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}

按照年龄排序:(改变比较函数即可)年龄是整型直接相减就可以判断元素的大小
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
char name[20];
int age;
};
int cmp_age(const void* e1, const void* e2)
{
return ((struct stu*)e1)->age-((struct stu*)e2)->age;
}
int main()
{
struct stu s[3] = { {"zhangsan",11},{"lisi",10},{"wangwu",12} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}

四、利用冒泡排序模拟实现qsort函数
#include <stdio.h>
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void swap(char* buf1, char* buf2,int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void Bubblesort(void* base,size_t num,size_t width,int(*cmp)(const void* e1, const void* e2))
{
size_t i = 0;
size_t j = 0;
for (j = 0; j < num - 1; j++)
{
for (i = 0; i < num - j - 1; i++)
{
if (cmp((char*)base+i*width,(char*)base+(i+1)*width) > 0)
{
swap((char*)base + i * width, (char*)base + (i + 1) * width, width);
}
}
}
}
int main()
{
int arr[10] = { 2,7,1,3,4,5,6,9,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubblesort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
上图代码为利用冒泡排序模拟实现的qsort函数
我们进行分析:
1.

我们看主函数的代码几乎一致,只有排序的函数名字改变了(变成我们自己编写的Bubblesort)
2.自定义Bubblesort函数
![]()
这里因为不知道所排列的数组是什么类型,数组的大小是多少,每个元素的大小,所以这里的参数类型我们直接采用qsort函数的参数样式即可

冒泡排序的趟数和元素个数编写方法都和普通的冒泡排序一致

这里的条件判断if语句与普通的冒泡语句中
的本条语句效果一致,即判断相邻两个元素大小
解析本条语句:
1.为什么强制类型转化使用的是char*
因为char*为最小的指针类型,char*+1类型每回跳过一个字节,因为我们不清楚所需要排序的数组类型,所以我们选择char*类型,最小并且是最细的方法,把数组的首元素强制类型转化后,后面加上 i*width(实际数组元素的大小),就是可以得到我们实际数组的元素地址。如果这里我们采用int*,int*+1跳过的是四个字节,如果排序的数组是char类型,这样的就获得不了数组的所有元素地址,所以我们需要采取最小最细的类型去对获取数组元素的地址
2.把我们获取的两个变量放入之前编辑好的比较函数中,比较两个数字的大小
3.如果满足我们的if条件句的条件我们就进入到元素交换的语句中来

本条语句与我们普通的冒泡排序中的
的效果一致就是交换两个元素的大小只不过我们利用一个函数去将换顺序这个功能实现。
4.swap函数
![]()
这里的参数我们采用两个指针去接收两个变量的地址,而且还需要元素的大小
交换元素的代码如下:

最终运行结果:
![]()
我们试一下结构体可不可以拿我们刚刚编写好的冒泡排序进行排序
#include <stdio.h>
void swap(char* buf1, char* buf2,int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void Bubblesort(void* base,size_t num,size_t width,int(*cmp)(const void* e1, const void* e2))
{
size_t i = 0;
size_t j = 0;
for (j = 0; j < num - 1; j++)
{
for (i = 0; i < num - j - 1; i++)
{
if (cmp((char*)base+i*width,(char*)base+(i+1)*width) > 0)
{
swap((char*)base + i * width, (char*)base + (i + 1) * width, width);
}
}
}
}
struct stu
{
char name[20];
int age;
};
int cmp_age(const void* e1, const void* e2)
{
return ((struct stu*)e1)->age-((struct stu*)e2)->age;
}
int main()
{
struct stu s[3] = { {"zhangsan",11},{"lisi",10},{"wangwu",12} };
int sz = sizeof(s) / sizeof(s[0]);
/*qsort(s, sz, sizeof(s[0]), cmp_name);*/
Bubblesort(s, sz, sizeof(s[0]), cmp_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}
按照年龄排

#include <stdio.h>
void swap(char* buf1, char* buf2,int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void Bubblesort(void* base,size_t num,size_t width,int(*cmp)(const void* e1, const void* e2))
{
size_t i = 0;
size_t j = 0;
for (j = 0; j < num - 1; j++)
{
for (i = 0; i < num - j - 1; i++)
{
if (cmp((char*)base+i*width,(char*)base+(i+1)*width) > 0)
{
swap((char*)base + i * width, (char*)base + (i + 1) * width, width);
}
}
}
}
struct stu
{
char name[20];
int age;
};
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int main()
{
struct stu s[3] = { {"zhangsan",11},{"lisi",10},{"wangwu",12} };
int sz = sizeof(s) / sizeof(s[0]);
Bubblesort(s, sz, sizeof(s[0]), cmp_name);
/*Bubblesort(s, sz, sizeof(s[0]), cmp_age);*/
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}
按姓名排:

总结
这些就是qsort函数一些使用方法,以及利用冒泡排序去模拟实现qsort函数
本文详细介绍了冒泡排序的基本原理,其局限性,并通过实例演示了C语言标准库函数qsort的使用,包括如何处理不同类型的数据排序。随后,展示了如何利用冒泡排序的思想模拟实现qsort,适用于不同数据结构的排序。
1281

被折叠的 条评论
为什么被折叠?



