C语言qsort函数实战指南:参数解析、回调实现与模拟
本文深入解析C语言标准库中的快速排序函数 qsort。涵盖其核心参数含义、回调函数的编写技巧,以及如何处理整型数组和复杂结构体的排序需求。文章还包含 qsort 的底层冒泡模拟实现,帮助理解指针运算在泛型排序中的应用。
回调函数机制
qsort 是 C 语言标准库中的一个通用排序函数,其核心依赖于回调函数。回调函数是一个通过函数指针被调用的函数。当函数的指针作为参数传递给另一个函数时,接收方利用该指针调用其所指向的函数,即构成回调。这种机制使得排序算法不需要知道具体数据的类型,只需通过回调函数返回的比较结果来指导排序逻辑。
qsort 函数原型解析
在使用 qsort 前,需明确其四个关键参数:
void* base:待排序数组的首元素地址。
size_t num:数组中元素的总个数。
size_t width:单个元素的大小(字节数),通常使用 sizeof(arr[0]) 获取。
int (*cmp)(const void*, const void*):比较函数的地址。该函数接收两个元素指针,并返回整数以指示顺序。
比较函数的返回值规则严格遵循以下标准:
1. 若返回值小于 0,表示第一个元素排在第二个元素之前(e1 < e2);
2. 若返回值等于 0,表示两元素相等;
3. 若返回值大于 0,表示第一个元素排在第二个元素之后(e1 > e2)。
整型数组排序示例
#include <stdlib.h>
#include <stdio.h>
// 自定义比较函数:升序排列
int int_cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 9,8,7,6,5,2,4,3,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
// 调用 qsort
qsort(arr, sz, sizeof(arr[0]), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]); // 输出: 0 1 2 3 4 5 6 7 8 9
}
return 0;
}由于 e1 和 e2 是无类型指针,直接解引用会导致编译错误。因此,必须在比较函数中将指针强制转换为具体类型(如 int*)后再进行解引用。此例展示了标准的回调流程:用户在主程序定义 int_cmp,而 qsort 内部负责调用它。
结构体数据排序
按结构体成员排序:字符串比较
以下代码演示对包含姓名和年龄的结构体数组,按姓名字典序排序。需注意 strcmp 的返回值特性:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
};
// 按姓名升序排序
int str_name_cmp(const void* e1, const void* e2)
{
// 强转为结构体指针后访问name字段
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int main()
{
struct Stu s[] = { {"zhangsan",18},{"lisi",17},{"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), str_name_cmp);
for (int i = 0; i < sz; i++)
{
printf("%s ", s[i].name); // 输出: lisi wangwu zhangsan
}
return 0;
}按结构体成员排序:整型比较
// 续接上方结构体定义
int str_age_cmp(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
// main函数中调用
// qsort(s, sz, sizeof(s[0]), str_age_cmp);
对于结构体内部的整型成员,处理方式与原生整型数组类似,关键在于先进行结构体类型的强制转换,再通过箭头操作符 > 访问目标字段。
qsort 模拟实现(冒泡排序版)
为了深入理解 qsort 的泛型特性,下面提供一个基于冒泡排序的模拟实现。重点在于处理无类型指针的字节级交换。
#include <stdlib.h>
#include <stdio.h>
void Swap(char* p1, char* p2, size_t width)
{
for (size_t i = 0; i < width; i++)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
void qsort_bubble(void* base, size_t sz, size_t width, int(*compare)(const void* e1, const void* e2))
{
for (size_t i = 0; i < sz - 1; i++)
{
int change = 0;
for (size_t j = 1; j < sz - i; j++)
{
// 计算当前相邻两个元素的地址
// 将base视为char*以便进行字节偏移运算
if (compare((char*)base + (j - 1) * width, (char*)base + j * width) > 0)
{
Swap((char*)base + (j - 1) * width, (char*)base + j * width, width);
change = 1;
}
}
if (change == 0)
break; // 优化:若无交换则提前结束
}
}
int main()
{
int arr[10] = { 9,8,7,6,5,2,4,3,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
// 使用模拟实现的冒泡排序
qsort_bubble(arr, sz, sizeof(arr[0]), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}在上述模拟实现中,Swap 函数接受字节指针 char*,通过循环逐个字节交换数据,从而实现了不依赖具体数据类型的通用交换逻辑。这也是库函数能够处理 struct、float 等各种类型的核心技术。
升降序控制逻辑
回顾 compare 函数:当返回正值(前 > 后)时触发交换,最终小的元素会沉到前面,形成升序。若要实现降序,只需在比较函数中反转判断逻辑(如返回 *val2 - *val1),使得大的元素交换至前方。
实际场景应用:LeetCode 977
在处理包含负数的数组平方排序问题时,qsort 是一种简洁的解决方案。先将所有元素平方,再统一排序:
#include <math.h>
int compare(const void* elem1, const void* elem2)
{
return *(int*)elem1 - *(int*)elem2;
}
int* sortedSquares(int* nums, int numsSize, int* returnSize){
*returnSize = numsSize;
for(int i=0; i<numsSize; i++)
{
nums[i] = pow(nums[i], 2);
}
qsort(nums, numsSize, sizeof(nums[0]), compare);
return nums;
}尽管 qsort 使用便捷,但在算法竞赛或特定性能敏感场景中,应根据数据特征选择更高效的排序策略(如双指针法)。以上内容涵盖了从基础用法到底层原理的完整知识链。
以上就是关于C语言库函数qsort的使用详解,更多关于C/C++排序算法的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









