我们知道qsort函数可以排任意类型的数据,原理是我们会给它传一个函数,通过那个函数,我们可以实现任意类型数据的排序。
qsort函数运用的是快速排序实现的,在这里我们使用冒泡排序实现一下,我们先单纯的写一个冒泡排序,
void bubble_sort(char* arr, int sz) {
for (int i = 0; i < sz-1; i++) {
for (int j = 0; j < sz - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
我们会发现冒泡排序会有缺点:
缺点一:只能比较整形数据,只能传整形数组。
解决办法:使用void*的指针,(因为void*的指针可以接收任意类型的指针,但是它不能进行+1-1的操作)这样就不知道数组的个数和元素的大小,所以要传num,size
缺点二:对于不同类型的数据,不能简单使用< >==比较
解决办法:将两个元素的比较方法,以函数参数的形式传递
缺点三:不同的类型的数据,交换略有差异
解决办法:写一个交换函数,逐个字节进行交换
下面我们就来实现一下:
void Swap(char* buf1, char* buf2, int sz) {
for (int i = 0; i < sz; i++) {
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_qsort(void* base, size_t num, size_t size, int(*cmp)(const void* e1, const void* e2)) {
//趟数
for (int i = 0; i < num-1; i++) {
for (int j = 0; j < num - 1 - i; j++) {//每趟需要排的次数
if ((cmp((char*)base + j * size, (char*)base + (j + 1) * size)) > 0) {//如果前边元素大于后边元素的话,就交换位置
Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);//交换函数
}
}
}
}
int cmp_int(const void* e1, const void* e2) {
return *(int*)e1 - *(int*)e2;
}
void print_arr(int* arr, int sz) {
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test1() {
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
print_arr(arr, sz);
bubble_qsort(arr, sz, sizeof(int), cmp_int);
print_arr(arr, sz);
}
int main() {
test1();//用来测试整型数组
return 0;
}
这就是我们模拟qsort函数写的一个排序的函数,前边两个函数就相当于是一个编写qsort函数的程序员写的,后面就是用户自己写的,去使用这个函数,这个模拟实现比较难理解的是什么呢?
if ((cmp((char*)base + j * size, (char*)base + (j + 1) * size)) > 0) {//如果前边元素大于后边元素的话,就交换位置
Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);//交换函数
}
我觉得就是这两段代码了。
因为cmp函数是用户自己写的,所以他知道要比较什么类型的数据,并且数据类型的大小也是知道的。我们在编写这个第一行时无非就是传过去两个地址嘛,别的不用管。那我们怎么找到一个不知道类型的数组中的一个元素的地址呢?
我们知道数组的起始地址,以及每个元素所占字节的大小,以及这是数组的第几个元素,但现在void*是不能±操作的,那么就强制类型转换成char*这样每次向后加一就访问一个字节,我们由知道加几个字节,这样就找到了元素的地址。
Swap函数的话是交换两个元素,我们就可以传过去每个元素的大小,然后在一个字节一个字节的交换就可以了。
这只是整形,那么能不能实现一下结构体的排序呢?
struct Stu {
char name[20];
int age;
};
int cmp_by_name(const void* e1, const void* e2) {
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int main() {
struct Stu arr[] = { {"zhangsan",32},{"lisi",12},{"wangwu",20}};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_qsort(arr, sz, sizeof(arr[0]), cmp_by_name);
for (int i = 0; i < sz; i++) {
printf("%s %d\n", arr[i].name, arr[i].age);
}
}
基于上面写的bubble_qsort函数我们写了这样一个代码,发现是可以排序的。
这是根据名字进行排序的,我们再试试根据年龄进行排序
struct Stu {
char name[20];
int age;
};
int cmp_by_age(const void* e1, const void* e2) {
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main() {
struct Stu arr[] = { {"zhangsan",32},{"lisi",12},{"wangwu",20}};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_qsort(arr, sz, sizeof(arr[0]), cmp_by_age);
for (int i = 0; i < sz; i++) {
printf("%s %d\n", arr[i].name, arr[i].age);
}
}
肯定也是可以实现的。