C语言 qsort 函数详解与冒泡排序模拟实现
本文深入解析 C/C++ 中 qsort 函数的使用规则与底层原理,涵盖任意类型数据的泛型排序实现,并通过代码演示如何结合回调函数与字节级交换操作,以冒泡排序思想模拟完成通用排序算法。
一、qsort 排序函数核心机制
qsort 是 C 标准库中用于快速排序的通用函数,位于 <stdlib.h> 头文件中。其核心价值在于通过 void* 指针和回调函数实现“泛型”排序,即不依赖具体数据类型即可完成数组排序。
// qsort 函数原型
#include <stdlib.h>
void qsort(void* base, size_t num, size_t size,
int (*compar)(const void*, const void*));
其中:
- base:待排序数组的首地址;
- num:数组元素个数;
- size:单个元素占用的字节数(sizeof(element));
- compar:指向自定义比较函数的指针,决定排序逻辑。
第1步:理解比较函数返回值含义
比较函数的签名通常为 int compar(const void *e1, const void *e2)。它决定了两个元素的相对顺序:
- 若返回 负数(< 0):认为 e1 排在 e2 之前;
- 若返回 零(= 0):两者相等,位置不变;
- 若返回 正数(> 0):认为 e1 排在 e2 之后。
第2步:配置不同类型数据的比较逻辑
1. 整数类型(升序示例):
int compare_ints(const void* p1, const void* p2)
{
// 将 void* 强转为 int*,再解引用比较
return *(const int*)p1 - *(const int*)p2;
}
2. 字符串/字符数组(按字典序):
#include <string.h>
int compare_strings(const void* p1, const void* p2)
{
return strcmp((const char*)p1, (const char*)p2);
}
3. 结构体(多关键字排序):
针对结构体,需优先判断主字段,相等后再判断次字段。例如学生信息排序:
struct Student {
char name[20];
int age;
};
int compare_students_by_name(const void* p1, const void* p2) {
const struct Student* s1 = (const struct Student*)p1;
const struct Student* s2 = (const struct Student*)p2;
return strcmp(s1->name, s2->name);
}
二、实战演练:qsort 调用规范
场景1:整型数组排序
直接传入数组名作为首地址,计算长度与元素大小即可:
int arr[] = { 3, 5, 2, 6, 8, 1 };
int sz = sizeof(arr) / sizeof(arr[0]);
// 升序排列
qsort(arr, sz, sizeof(arr[0]), compare_ints);
// 若要降序,只需在比较函数中反转差值:
// return *(const int*)p2 - *(const int*)p1;
场景2:结构体按成员字段排序
同样适用上述泛型接口,只需确保比较函数能正确转换结构体指针:
struct Student students[] = {
{"Alice", 20}, {"Bob", 22}
};
int count = 2;
// 按年龄升序
qsort(students, count, sizeof(struct Student),
[](const void* a, const void* b){
return ((Student*)a)->age - ((Student*)b)->age;
});
三、深度解析:用冒泡算法模拟 qsort 实现
虽然内部实现多采用快排,但为理解 void* 的灵活性,可用冒泡排序的思想重构一个支持泛型的排序器。重点难点在于通用数据交换。
第1步:设计泛型冒泡框架
模仿 qsort 接口,参数包括起始指针、数量、单元素字节宽度和比较函数指针:
void generic_bubble_sort(void* base, int num, int width,
int (*cmp)(const void*, const void*)) {
for (int i = 0; i < num - 1; i++) {
for (int j = 0; j < num - i - 1; j++) {
// 关键步骤:计算相邻元素内存地址
void* elem1 = (char*)base + j * width;
void* elem2 = (char*)base + (j + 1) * width;
if (cmp(elem1, elem2) > 0) {
// 如果顺序错误,则交换
swap_bytes(elem1, elem2, width);
}
}
}
}
注释:
将 void* 转换为 char* 是处理字节偏移的关键。因为 char 占 1 字节,乘以 j * width 可直接定位到第 j 个元素的起始内存位置,无需关心数据类型。
第2步:实现按字节搬运的通用交换函数
由于不知道具体数据类型大小,只能按字节拷贝交换:
void swap_bytes(char* buf1, char* buf2, int width) {
char temp;
for (int k = 0; k < width; k++) {
temp = buf1[k];
buf1[k] = buf2[k];
buf2[k] = temp;
}
}第3步:完整测试与验证
整合上述逻辑,对整型数组进行排序验证:
// 测试代码
int test_arr[] = {9, 1, 5, 3};
generic_bubble_sort(test_arr, 4, sizeof(int), compare_ints);
// 输出结果应为: 1 3 5 9
该方法不仅适用于 int,只要提供对应的比较函数,同样可完美支持结构体或复杂对象的排序,展示了 C 语言中指针操作的威力。
总结
以上就是 C/C++ qsort 函数排序应用与冒泡模拟实现的详细内容,更多关于 C++ 回调函数与泛型排序技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









