在C语言的标准库中,qsort函数是实现通用排序的核心工具,它支持对任意类型的数据数组进行排序,而排序的核心逻辑就依赖用户传入的自定义比较方法。理解比较方法的编写规则,就能应对各种排序需求。

qsort函数的基本参数
qsort函数定义在<stdlib.h>头文件中,其函数原型如下:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
各个参数的含义如下:
- base:指向待排序数组首元素的指针
- nmemb:数组中元素的个数
- size:数组中每个元素的字节大小
- compar:指向自定义比较函数的指针,这个函数就是我们需要编写的核心部分
自定义比较方法的编写规则
比较函数需要接收两个const void *类型的参数,这两个参数分别指向待比较的两个数组元素。函数返回值的规定如下:
| 返回值 | 含义 |
|---|---|
| 小于0 | 第一个参数指向的元素应该排在第二个参数指向的元素前面 |
| 等于0 | 两个元素相等,排序顺序无要求 |
| 大于0 | 第一个参数指向的元素应该排在第二个参数指向的元素后面 |
编写比较函数的核心步骤是:先将void *类型的参数转换为对应数据类型的指针,再解引用获取元素值进行比较,最后返回比较结果。
不同场景的自定义比较方法示例
场景1:整数数组升序排序
对int类型数组进行升序排序时,比较函数实现如下:
#include <stdio.h>
#include <stdlib.h>
// 整数升序比较函数
int int_compare_asc(const void *a, const void *b) {
// 将void*转换为int*,再解引用获取值
int num1 = *(const int *)a;
int num2 = *(const int *)b;
// 升序:前小后大返回负数,相等返回0,前大后小返回正数
if (num1 < num2) {
return -1;
} else if (num1 == num2) {
return 0;
} else {
return 1;
}
}
// 简化写法,直接返回两数差值
int int_compare_asc_simple(const void *a, const void *b) {
return *(const int *)a - *(const int *)b;
}
int main() {
int arr[] = {5, 2, 8, 1, 9};
int n = sizeof(arr) / sizeof(arr[0]);
// 调用qsort排序
qsort(arr, n, sizeof(int), int_compare_asc_simple);
// 打印排序结果
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("n");
return 0;
}
场景2:整数数组降序排序
降序排序只需要调整比较结果的逻辑,把两个元素的顺序调换即可:
#include <stdio.h>
#include <stdlib.h>
// 整数降序比较函数
int int_compare_desc(const void *a, const void *b) {
// 降序:前大后小返回负数,相等返回0,前小后大返回正数
return *(const int *)b - *(const int *)a;
}
int main() {
int arr[] = {5, 2, 8, 1, 9};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), int_compare_desc);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("n");
return 0;
}
场景3:结构体数组按指定字段排序
假设我们有一个学生结构体,需要按学生的成绩升序排序,比较函数需要处理结构体指针:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义学生结构体
typedef struct {
char name[20];
int score;
} Student;
// 按成绩升序比较结构体
int student_compare_by_score(const void *a, const void *b) {
// 转换为Student*类型指针
const Student *s1 = (const Student *)a;
const Student *s2 = (const Student *)b;
// 按score字段比较
return s1->score - s2->score;
}
int main() {
Student students[] = {
{"张三", 85},
{"李四", 92},
{"王五", 78}
};
int n = sizeof(students) / sizeof(students[0]);
qsort(students, n, sizeof(Student), student_compare_by_score);
for (int i = 0; i < n; i++) {
printf("姓名:%s,成绩:%dn", students[i].name, students[i].score);
}
return 0;
}
场景4:字符串数组排序
排序字符串数组时,需要比较字符串内容,可以使用标准库的strcmp函数:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 字符串升序比较函数
int str_compare_asc(const void *a, const void *b) {
// 注意:数组元素是char*类型,所以a和b指向的是char*,需要转换为char**再解引用
const char *str1 = *(const char **)a;
const char *str2 = *(const char **)b;
return strcmp(str1, str2);
}
int main() {
char *arr[] = {"apple", "banana", "cherry", "date"};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(char *), str_compare_asc);
for (int i = 0; i < n; i++) {
printf("%s ", arr[i]);
}
printf("n");
return 0;
}
注意事项
- 比较函数的两个参数必须是
const void *类型,不能随意修改参数指向的内容 - 转换指针类型时要匹配数组实际的元素类型,否则会出现数据读取错误
- 当比较返回值使用两数相减的方式时,要注意数据溢出问题,比如对很大的整数做减法可能超出int范围,此时建议使用分支判断的写法
- 如果排序需求是降序,只需要把两个比较参数的位置互换即可,不需要修改其他逻辑