函数指针的应用(二)模仿qsort的功能实现通用的冒泡排序

仿照库函数qsort(),来实现一个通用的冒泡排序

qsort()是快速排序,库函数中的qsort()能对多种类型进行排序。
为了使冒泡排序也能对多种类型排序,我们首先需要了解一下qsort()的各项参数:
void qsort(void *base, size_t num, size_t width, int(__cdecl *compare)(const void *elem1, const void *elem2)
  • void *base:表示要排序目标的起始位置
  • size_t num:表示要排序元素的个数
  • size_t width: 表示每个元素的所占长度,单位是字节。 因为并不知道要排序的元素类型(字符串,数字或是其他),所以当第一次比较完之后,无法定位到下一次应该比较哪个位置的元素,所以很有比较知道
    每个元素的长度,每次跳过该长度就能来到下一个元素的位置,比如整型,每次跳过4字节,来到下一个元素,结构体,每次跳过结构体中的元素的长度,便能指向下一个结构体元素。

  • int(__cdecl *compare)(const void *elem1, const void *elem2): 是个函数指针,由于元素类型决定了元素之间的比较方式(字符串用strcmp()比较,数字可以直接比较),而函数库的设计者并不能知道
    用户要比较什么类型的元素,所以这一部分需要由用户来设计函数,使用时通过函数指针调用用户所编写的函数

    冒泡排序函数中的参数参照上面。


通用冒泡排序代码:
#include<stdlib.h>
#include<string.h>
#include<assert.h>


struct stu
{
    char name[20];
    int age;
};

int cmp_int(const void *e1, const void *e2)  //按照整型大小排序
{
    return *(int *)e1 - *(int *)e2;
}

int cmp_stu_by_name(const void *e1, const void *e2)   //按照姓名排序
{
    assert(e1 && e2);
    int ret = 0;
    ret = strcmp(((struct stu *)e1)->name,((struct stu *)e2)->name);  //strcmp 函数用来比较两个字符串大小,从第一个字符开始比,第一个字符相等时才继续往后比。 
                                                                      // 前者大,返回值大于0;前者小,返回值小于0;一样大,返回0;
    return ret;                                                      
}

int cmp_stu_by_age(const void *e1, const void *e2)   //按照年龄排序
{
    assert(e1 && e2);
    return  ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}

上面代码是用户根据需求编写的排序函数,下面是程序员设计的,可以接收用户设计的函数地址的函数。

void Swap(char *p1, char *p2, size_t width)
{
    assert(p1 && p2);
    size_t i = 0;
    for (i = 0; i < width; i++) // 逐个字节进行交换
    {
        char tmp = *p1;
        *p1 = *p2;
        *p2 = tmp;
        p1++;
        p2++;
    }   
}

void bubble_sort(void *base, size_t num, size_t width, int(*cmp)(const void *e1, const void *e2))  // cmp是 函数指针, 存放函数的地址,可以通过地址找到(调用)函数。 
{
    assert(base && cmp);
    size_t i = 0;
    size_t j = 0;
    for (i = 0; i < num - 1; i++)   // 冒泡排序  每趟能确定一个元素的正确位置,一共要排 元素个数-1趟
    {
        for (j = 0; j < num-1-i; j++)     //每一趟冒泡排序中,两两比较的次数
        {
            if ((cmp((char*)base + j*width, (char*)base +(j+1)* width) )> 0)    // base 代表元素首地址。 (char*)base + j*width:确定j*元素的宽度,表示第j个元素 
                                                                                // 函数指针cmp 调用 cmp_stu_by_age,将返回值和0比较
            {
                Swap((char*)base + j * width, (char*)base + (j+1) * width, width);  //  当前面一个元素 大于后面的元素,交换位置
            }
        }
    }
}


int main()
{
    //int arr[] = { 2,8,6,7,5,0,9,7,1,3};
    struct stu arr[] = { {"bang",20},{"uzi",18} ,{"faker",19} };
    int sz = sizeof(arr) / sizeof(arr[0]);
    //qsort(arr, sz, sizeof(arr[0]),cmp_int); 
    //qsort(arr, sz, sizeof(arr[0]),cmp_stu_by_name); 
    //qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
    bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);   //按照名字排序
    for (int i = 0; i < 3; i++)
    {
        printf("%s %d\n", arr[i].name, arr[i].age);
    }
    system("pause");
    return 0;
}

结果:
按照年龄排序:这里写图片描述
按照名字排序:这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值