qsort :超级打包工
你可以把qsort想象成一个**“超级打包工”**。
它的绝活是能把一堆乱七八糟的东西(数组)排得整整齐齐。但它有个“怪癖”:它力气很大,能搬任何东西(整数、小数、结构体),但它不认识这些东西,不知道谁大谁小。
所以,每次用它干活,你都得配一个**“指导员”**(比较函数),告诉它:“嘿,这两个东西,谁应该放前面?”
1. 怎么“雇佣”这个打包工?(函数原型)
要请qsort帮忙,你得把话说全了,一共要给它 4 个信息:
void qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*));这 4 个参数翻译成人话就是:
base(基地):要排序的数组,从哪儿开始?(传数组首地址)。num(人数):一共有多少个元素要排?size(体型):每个元素占多大内存?(比如int是 4 字节,用sizeof(int))。compar(指导员):这是一个函数指针。你得把你自己写的比较函数的地址传给它。
2. 怎么写“指导员”?(比较函数)
这是新手最容易晕的地方。qsort会拿着数组里的两个元素的地址,去问你的“指导员”函数:“这俩谁大?”
标准模板:
int cmp_function(const void* e1, const void* e2) { 你的逻辑 }核心规则(死记硬背版):
- 如果
e1比e2大,返回正数(> 0)。 - 如果
e1比e2小,返回负数(< 0)。 - 如果
e1等于e2,返回0。 - 要注意的是qsort是默认升序的。
这就是为什么qsort函数的第四个参数的函数指针所指向的函数的返回类型是int
实战技巧(整型排序):
如果是排整数,想从小到大排,直接做减法最简单:return *(int*)e1 - *(int*)e2;
(注意:这里必须先把void*强转成int*,再解引用取值,因为void指针不能解引用)
3. 一个完整的栗子(排序整数)
假设我们要把{ 9, 3, 5, 1 }排个序。
#include <stdio.h> #include <stdlib.h> qsort 在这里面 第一步:写个指导员函数(从小到大) int int_cmp(const void* e1, const void* e2) { 强转类型,然后做减法 return *(int*)e1 - *(int*)e2; } int main() { int arr[] = { 9, 3, 5, 1 }; int sz = sizeof(arr) / sizeof(arr[0]); 算出有4个元素 第二步:雇佣 qsort 参数1:数组名(首地址) 参数2:元素个数 (4) 参数3:单个元素大小 (4字节) 参数4:比较函数的名字(地址) qsort(arr, sz, sizeof(int), int_cmp); 打印看看 for(int i = 0; i < sz; i++) { printf("%d ", arr[i]); } 输出结果:1 3 5 9 return 0; }4. 避坑指南(重点!)
void是个“瞎子”*:
qsort传给你的e1和e2都是void*类型。你不能直接对它们解引用(比如*e1是错的),也不能直接加减(e1+1是错的)。
必须先“整容”(强制类型转换)!
比如你要比整数,就得先写成(int*)e1,然后再*(int*)e1取值。结构体怎么排?
如果数组里是学生结构体,你想按年龄排。
你的比较函数里,就要把void*转成struct Stu*,然后比较->age。return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;字符串类型的数据要用strcmp,详见:strcmp介绍
总结一下:qsort负责干活(搬运、交换),你负责动脑(写比较逻辑)。只要你的比较函数写对了,它就能帮你排任何数据!
