谈到了qsort (quick sort)库函数,我们就先来了解一下,qsort 是一个用来排序的库函数,底层原理是通过快速排序来进行的.他的功能并不像冒泡排序一样只能排整数,它可以进行任意类型的排序(类型您来定)
qsort函数的认识
在MSDN上查一下
需要的头文件有两个(其中一个就可以)
无返回值,有四个参数,类型分别是无类型指针、无符号整型、无符号整型、函数指针.并且这个函数指针的两个参数类型都是常量我类型指针,返回类型是整型。
名词介绍
待排序的数组的起始地址。
待排序数组元素的个数 。
待排序数组每个元素的大小(字节单位)。
指向一个可以比较两个元素大小的函数(您想让他按照什么标准进行比较)
参数四解析
参数四是由一个函数指针接收,故调用是需要传一个函数地址进去,qsort既然是排序库函数,则肯定就少不了比较大小这一项,可是比较结果怎么展示呢,难道像冒泡排序一样用 > < = 吗,当然不是,否则功能不就被限制于比较数字大小了吗,所以就由返回值来判断大小,返回的数字与0比较,从而判断大小。
用qsort库函数实现冒泡排序
#include<stdlib.h>
#include<stdio.h>
int com(const void* p1, const void* p2)//p1与p2实际就是arr中待比较的元素
{
return *(int*)p1 - *(int*)p2;//?
}
int main()
{
//void qsort(void* base, size_t num,
// size_t width,
// int(__cdecl * compare)(const void* elem1, const void* elem2));
int arr[10] = { 3,7,5,9,0,2,4,6,8,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), com);//传自己定的函数com
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
这里为什么要强制类型转换呢,在博主之前的文章之中也曾提到解引用(*)操作所访问的空间取决于它的类型,但是它是一个void* 的类型,无具体类型指针,所以在解引用(*)时,编译器就很困惑,不知访问几个字节的空间,因此也不可以 + - 整数。所以就有把 p 强制类型转换成 int* 。
那么 void* 为什么不换成 int* 呢,那就是开发者的智慧了,因为你不一定是比较整型啊,可能比较什么其它类型的嘛。所以就统一用 void* 更合理。
qsort库函数是默认排成升序的,想要排成降序就将 p1 与 p2 的位置交换一下。
用qsort库函数实现结构体排序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct ide
{
char name[20];
short age;
};
int cmp(const void* p1,const void* p2 )//固定形式
{
//return (*(struct ide*)p1).age - ((struct ide*)p2)->age;//结构体的两种写法
return strcmp((*(struct ide*)p1).name, ((struct ide*)p2)->name);//比较字符用strcmp
}
int main()
{
struct ide man[] = { { "zhangsan",18 },{ "lisa",37 }, { "wanghu",23 } };//结构体数组
int sz = sizeof(man) / sizeof(man[0]);
qsort(man, sz, sizeof(man[0]), cmp);
for (int i = 0; i < sz; i++)
{
printf("%d %s\n", man[i].age,man[i].name);//man[i]就是对应的结构体
}
return 0;
}
升级冒泡排序
我们想让冒泡函数也有 qsort 函数一样的功能那么就要借鉴一波 qsort 函数的实现了。
#include<stdio.h>
#include<string.h>
void swap(char* a, char* b,size_t n)//通过单位大小知道访问多少字节空间
{
for (int i = 0; i < n; i++)
{
char tmp = *a;
*a = *b;
*b = tmp;
a++; b++;
}
}
void pul(void* base, size_t num, size_t width, int(*pc)(void* p1, void* p2))
{
int i, j;
for (i = 0; i < num-1; i++)//冒泡次数
{
for (j = 0; j < num - 1 - i; j++)//每一次
{
if (pc((char*)base+j*width,(char*)base+(j+1)*width)>0)//强转成char*的指针,好计算跳过的字节个数,大于0就交换,故达到了升序目的
{
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);//将单位大小也传过去
}
}
}
}
struct ide
{
char name[20];
short age;
};
int com(const void* p1, const void* p2)//这是你自己需要写的函数,给一个比较标准
{
//return *(int*)p1 - *(int*)p2;
return strcmp((*(struct ide*)p1).name, (*(struct ide*)p2).name);
}
int main()
{
struct ide arr[] = { {"wanggang",25},{"lihu",18},{"niuma",32} };
int sz = sizeof(arr) / sizeof(arr[0]);
pul(arr, sz, sizeof(arr[0]), com);//自己定义一个函数pul实现qsort的功能。
for (int i = 0; i < sz; i++)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
return 0;
}
这里其实使用到了回调函数,回调函数在这里就不说了,有很多其它博主都讲得超详细的。