C语言 qsort 函数详解与冒泡排序模拟实现

顾星河
顾星河
发布时间:2026-10-15 | 5人浏览过
原创

本文深入解析 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++ 回调函数与泛型排序技巧的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表