为排序而生的函数--qsort函数--万物皆可排序

深入解析qsort函数:从void*到比较函数精要

一、qsort函数原型解析

#include 
void qsort(
    void *base,          // 待排序数组首地址
    size_t nmemb,        // 数组元素个数
    size_t size,         // 单个元素大小(字节)
    int (*compar)(const void *, const void *)  // 比较函数指针
);

参数详解表

参数 类型 作用描述
base void* 指向待排序数组的起始位置
nmemb size_t 数组元素个数
size size_t 每个元素占用的内存字节数
compar 函数指针 决定排序顺序的关键函数

二、void*指针的三重境界

1. 通用性

  • 可指向任意类型数据
  • 示例:
    int arr_int[5] = {5,3,4,1,2};
    struct Person people[10]; 
    qsort(arr_int, 5, sizeof(int), compare_int);    // 排序整型数组
    qsort(people, 10, sizeof(struct Person), compare_person); // 排序结构体数组
    

2. 内存操作

  • 需要配合size参数进行元素定位
  • 元素访问公式:
    void* elem = (char*)base + i*size;  // 第i个元素地址
    

3. 类型安全

  • 必须在使用时进行显式类型转换
  • 错误示例:
    int* p = base;  // 错误!不能直接赋值void*
    

三、比较函数编写精要

基础模板

int compare(const void *a, const void *b) {
    const T *ptr1 = (const T*)a;  // T为实际数据类型
    const T *ptr2 = (const T*)b;
    
    // 比较逻辑
    if(/* a应排在b前 */) return -1;
    if(/* a应排在b后 */) return 1;
    return 0;
}

不同类型实现示例

1. 整型数组排序
int compare_int(const void *a, const void *b) {
    // 正确写法:避免减法溢出
    int num1 = *(const int*)a;
    int num2 = *(const int*)b;
    return (num1 > num2) - (num1 < num2);
}
2. 字符串数组排序
int compare_str(const void *a, const void *b) {
    return strcmp(*(const char**)a, *(const char**)b);
}
3. 结构体排序
typedef struct {
    int id;
    char name[32];
    double salary;
} Employee;

// 按薪资降序排序
int compare_employee(const void *a, const void *b) {
    const Employee *e1 = (const Employee*)a;
    const Employee *e2 = (const Employee*)b;
    
    if(e1->salary < e2->salary) return 1;
    if(e1->salary > e2->salary) return -1;
    return 0;
}

四、常见错误与防御性编程

错误类型表

错误类型 错误示例 正确写法
直接解引用void* int diff = *(int*)a - *(int*)b; 使用中间变量转换
错误的指针转换 char** s = (char**)a; *(char**)a
忽略大小端问题 直接memcpy数值比较 统一字节序处理
修改原始数据 在比较函数中修改元素值 使用const修饰指针

防御性技巧

int safe_compare(const void *a, const void *b) {
    // 1. 空指针检查
    assert(a != NULL && b != NULL);
    
    // 2. 边界值处理
    if(a == b) return 0;  // 相同地址快速返回
    
    // 3. 类型安全转换
    const DataType *p1 = (const DataType*)a;
    const DataType *p2 = (const DataType*)b;
    
    // 4. 安全比较
    return (*p1 > *p2) - (*p1 < *p2); 
}

五、高级应用场景

1. 多级排序

// 先按姓名升序,再按ID降序
int compare_multilevel(const void *a, const void *b) {
    const Employee *e1 = (const Employee*)a;
    const Employee *e2 = (const Employee*)b;
    
    int name_cmp = strcmp(e1->name, e2->name);
    if(name_cmp != 0) return name_cmp;
    
    return (e2->id - e1->id);
}

2. 泛型查找

// 使用bsearch需要相同比较函数
void* result = bsearch(key, array, nmemb, size, compare_func);

3. 自定义排序规则

// 按字符串长度排序
int compare_strlen(const void *a, const void *b) {
    const char *s1 = *(const char**)a;
    const char *s2 = *(const char**)b;
    return strlen(s1) - strlen(s2);  // 注意可能溢出!
}

关键要点总结

  1. void*是通用指针,需显式类型转换
  2. 比较函数应返回-1/0/1,而非直接差值
  3. 注意内存对齐和指针运算
  4. 防御性编程确保健壮性

你可能感兴趣的:(c语言,数据结构,算法,排序算法)