qsort 是 C 标准库中提供的通用快速排序函数 ,定义在 <stdlib.h> 头文件中。它的核心优势是支持对任意类型的数组(int、char、字符串、结构体等)进行排序,通过函数指针实现类型无关的比较逻辑,是 C 语言中最常用的排序工具之一。
一、函数原型
cs
void qsort(
void *base, // 待排序数组的起始地址
size_t nitems, // 数组中元素的个数
size_t size, // 单个元素的字节大小
int (*compar)(const void *, const void *) // 比较函数的指针
);
二、参数详细解析
| 参数 | 类型 | 说明 |
|---|---|---|
base |
void* |
指向待排序数组的首地址,void* 支持任意类型数组(int/char/ 结构体等) |
nitems |
size_t |
数组元素的总个数,size_t 是无符号整数类型(通常等价于 unsigned int) |
size |
size_t |
单个元素的字节大小,通常用 sizeof(元素类型) 获取(如 sizeof(int)) |
compar |
函数指针 int (*)(const void*, const void*) |
自定义比较函数,决定排序规则(升序 / 降序 / 按结构体字段排序) |
三、核心:比较函数 compar
qsort 的排序逻辑完全由比较函数决定,需遵循以下规则:
1. 函数格式
cs
int compar(const void *a, const void *b);
a/b:指向待比较的两个数组元素(const保证不修改原数据);- 返回值规则:
- 返回
< 0:a应排在b前面(a < b); - 返回
0:a和b相等; - 返回
> 0:a应排在b后面(a > b)。
- 返回
2. 关键注意点
void* 指针不能直接解引用,必须先强制转换为数组元素的实际类型,再解引用比较。
3. 常见比较函数示例
示例 1:int 数组(升序 / 降序)
cs
// 升序(a < b 时返回负数)
int compare_int_asc(const void *a, const void *b) {
// 转换为 int* 后解引用
int num1 = *(const int*)a;
int num2 = *(const int*)b;
return num1 - num2; // 升序:小的在前
}
// 降序(a > b 时返回负数)
int compare_int_desc(const void *a, const void *b) {
int num1 = *(const int*)a;
int num2 = *(const int*)b;
return num2 - num1; // 降序:大的在前
}
示例 2:字符串数组(按字典序排序)
cs
#include <string.h> // 需引入 strcmp
// 字符串数组的元素是 char*,因此 a/b 是 char** 类型
int compare_str(const void *a, const void *b) {
const char *str1 = *(const char**)a;
const char *str2 = *(const char**)b;
return strcmp(str1, str2); // strcmp 天然符合 qsort 返回规则
}
示例 3:结构体数组(按结构体字段排序)
cs
// 定义结构体
typedef struct {
char name[20];
int age;
} Person;
// 按年龄升序排序
int compare_person_age(const void *a, const void *b) {
const Person *p1 = (const Person*)a;
const Person *p2 = (const Person*)b;
return p1->age - p2->age;
}
// 按名字字典序排序
int compare_person_name(const void *a, const void *b) {
const Person *p1 = (const Person*)a;
const Person *p2 = (const Person*)b;
return strcmp(p1->name, p2->name);
}
四、完整使用示例
示例 1:排序 int 数组
cs
#include <stdio.h>
#include <stdlib.h>
// 升序比较函数
int compare_int_asc(const void *a, const void *b) {
return *(const int*)a - *(const int*)b;
}
int main() {
int arr[] = {5, 2, 9, 1, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
// 调用 qsort
qsort(arr, n, sizeof(int), compare_int_asc);
// 输出排序结果:1 2 5 5 6 9
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
示例 2:排序字符串数组
cs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_str(const void *a, const void *b) {
return strcmp(*(const char**)a, *(const char**)b);
}
int main() {
const char *strs[] = {"banana", "apple", "orange", "grape"};
int n = sizeof(strs) / sizeof(strs[0]);
qsort(strs, n, sizeof(char*), compare_str);
// 输出:apple banana grape orange
for (int i = 0; i < n; i++) {
printf("%s ", strs[i]);
}
return 0;
}
示例 3:排序结构体数组
cs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[20];
int age;
} Person;
// 按年龄升序
int compare_person_age(const void *a, const void *b) {
const Person *p1 = (const Person*)a;
const Person *p2 = (const Person*)b;
return p1->age - p2->age;
}
int main() {
Person people[] = {
{"Alice", 25},
{"Bob", 20},
{"Charlie", 30}
};
int n = sizeof(people) / sizeof(people[0]);
qsort(people, n, sizeof(Person), compare_person_age);
// 输出:Bob(20) Alice(25) Charlie(30)
for (int i = 0; i < n; i++) {
printf("%s(%d) ", people[i].name, people[i].age);
}
return 0;
}
五、注意事项
- void 转换*:必须严格转换为数组元素的实际类型,否则会导致内存错误(如 int 数组转 char* 会读取错误字节);
- size_t 类型 :
nitems和size是无符号数,不能传入负数; - 排序稳定性 :
qsort是不稳定排序(相同值的元素相对位置可能变化),若需稳定排序需自行实现
int compare_int_safe(const void *a, const void *b) {
int x = *(const int*)a;
int y = *(const int*)b;
return (x > y) - (x < y); // 无溢出风险
}
- 底层实现 :标准未规定具体排序算法,多数编译器(如 glibc)的
qsort是「快速排序 + 插入排序 + 堆排序」的混合实现(优化小数据量和最坏情况)。
总结
qsort 是 C 语言中灵活且高效的通用排序函数,核心是通过自定义比较函数适配任意类型数组。使用时只需关注:
- 正确传递数组地址、元素个数、元素大小;
- 编写符合规则的比较函数(重点是
void*类型转换)。