深入解析C语言qsort排序原理
大家好,这篇文章将带你深度理解 C 语言 qsort 排序的底层原理,从内存布局、比较函数调用,到元素交换和排序算法逻辑,逐步解析,让初学者也能看懂。
如果你觉得文章对你有帮助,欢迎 点赞支持 ,我会持续更新更多 算法与数据结构相关内容。
同时,如果你喜欢我的内容,可以 关注我/订阅专栏,不错过后续更多实用教程和深度解析。
qsort 是 C 标准库 <stdlib.h> 提供的通用排序函数,它可以对任意类型的数组进行排序。
函数原型:
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*));
| 参数 | 含义 |
|---|---|
base | 指向数组首元素的指针,类型是 void*,所以可以是任意类型数组 |
nitems | 数组中元素的数量 |
size | 每个元素的字节大小(sizeof(元素类型)) |
compar | 比较函数指针,接受两个 const void* 参数,返回值为整数,规定如下:<0 表示第一个元素小于第二个0 表示相等>0 表示第一个元素大于第二个 |
compar 模板
整数升序排序
int cmp_int(const void *a, const void *b) {
// 先转换类型
int x = *(int*)a;
int y = *(int*)b;
// if (x < y) {
// return -1;
// }
// else if (x > y) {
// return 1;
// }
//
// return 0;
return x - y; // 升序排序
// 如果要降序呢?调换一下位置即可
// return y - x;
}
通过解析
cmp_int理解compar我们要做的是:用
qsort排序整数数组。函数原型:
int cmp_int(const void *a, const void *b)Ⅰ为什么参数是
const void*
qsort是一个 通用排序函数,可以排序任何类型的数组:整数、浮点数、结构体等等。void*表示 指向任意类型的指针。const表示 函数不会修改数组里的值,只是比较它们大小。也就是说,
a和b是 指向数组元素的地址,但函数不会改变它们的内容。Ⅱ先把指针变成我们能用的整数
int x = *(int*)a; int y = *(int*)b;逐步解释:
(int*)a→ 把void*转成int*,告诉编译器这是一个整数地址*(int*)a→ 取出这个地址上的整数,也就是数组里的真实数字- 同理,
y = *(int*)b;也取出第二个数字Ⅲ比较两数
方式一:原型形式
if (x < y) return -1; // x 小于 y → 排在前面 else if (x > y) return 1; // x 大于 y → 排在后面 else return 0; // x 等于 y → 不动解释:
qsort通过返回值判断两个元素的顺序<0→ a 在前0→ 不变>0→ b 在前方式二:简化写法(可能溢出)
return x - y;
- 直接用整数相减得到结果
- 简洁,但如果数字很大或很小,可能溢出(计算结果超出整数范围),排序就错了
方式三:优化(推荐)
return (x > y) - (x < y);
(x > y)→ 如果 x > y 返回 1,否则 0(x < y)→ 如果 x < y 返回 1,否则 0- 减法得到 -1、0、1 → 完美对应
qsort规则- 没有溢出风险,代码既优雅又安全
Ⅳ示例
#include <stdio.h> #include <stdlib.h> int cmp_int(const void *a, const void *b) { int x = *(int*)a; int y = *(int*)b; return (x > y) - (x < y); } int main() { int arr[] = {5, 2, 9, 1}; int n = sizeof(arr)/sizeof(arr[0]); qsort(arr, n, sizeof(int), cmp_int); for (int i = 0; i < n; i++) printf("%d ", arr[i]); return 0; }输出:
1 2 5 9
浮点数降序排序
int cmp_double_desc(const void *a, const void *b) {
// 先转换类型
double x = *(double*)a;
double y = *(double*)b;
return (y > x) - (y < x); // 返回正数/负数/0
}
提问:浮点数排序可以使用
return x - y吗?答案是不可以
int cmp_float(const void *a, const void *b) { double x = *(const double*)a; double y = *(const double*)b; return x - y; // 错误 }原因
- 返回值类型错误
qsort比较函数必须返回intx - y是double,会被强制转换为int- 小差值(如 0.0001)转换成
int会变成 0 → 排序错误- 浮点精度问题
- 浮点数很接近,减法可能丢失精度
- 导致相近的两个元素被认为相等 → 排序结果不正确
字符串数组排序
int cmp_str(const void *a, const void *b) {
// 先转换类型
const char *str1 = *(const char **)a;
const char *str2 = *(const char **)b;
return strcmp(str1, str2);
}
结构体排序
typedef struct {
int a;
int b;
} Item;
int cmp_a_b(const void *x, const void *y) {
// 先转换类型
const Item *i1 = (const Item*)x;
const Item *i2 = (const Item*)y;
// 常用
if (i1->a != i2->a)
return i1->a - i2->a; // a 升序
return i1->b - i2->b; // a 相同则 b 升序
// 防溢出优化
// if (i1->a != i2->a)
// return (i1->a > i2->a) - (i1->a < i2->a); // a 升序
//
// return (i1->b > i2->b) - (i1->b < i2->b); // a 相同则 b 升序
}
内部排序原理
假设我们要排序一个结构体数组:
typedef struct {
int a;
int b;
} Item;
调用方式:
qsort(arr, n, sizeof(Item), cmp_a_b);
内部算法选择
C 标准库 qsort 的具体实现因平台而异,但通常采用 混合排序算法:
- 快速排序 (Quicksort)
- 平均时间复杂度 O(n log n)
- 通过基准分区,把小的放左,大的放右
- 对递归深度过大或接近有序数组可能退化
- Introsort / 混合排序
- 先用快速排序
- 当递归深度过深 → 切换为堆排序,保证最坏 O(n log n)
- 小数组使用插入排序,减少递归开销
比较函数调用机制
qsort通过传入指针调用比较函数:
cmp_a_b(&arr[i], &arr[j]);
- 内部操作步骤:
void*转Item*- 取出需要比较的字段(例如
a, b) - 返回
-1/0/1表示排序顺序
注意:比较函数只读指针,不修改数组,只提供顺序信息。
元素交换机制
- 当比较结果显示顺序需要调整时,
qsort交换元素:
swap(void *p1, void *p2, size_t size) {
char tmp[size];
memcpy(tmp, p1, size); // 临时保存 p1
memcpy(p1, p2, size); // p1 = p2
memcpy(p2, tmp, size); // p2 = p1
}
- 对结构体数组:
- 每次交换整个结构体内存块(包括所有字段)
排序流程(以快速排序为例)
- 选择基准元素 (pivot)
- 例如选择第一个或中间元素
- 分区
- 遍历数组,把小于 pivot 的元素放左边,大于 pivot 的放右边
- 递归
- 分别对左半部分和右半部分递归排序
- 优化
- 小数组 → 插入排序
- 递归过深 → 堆排序
362

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



