C语言进阶篇 第二讲【回调函数之模拟实现qsort函数】

一、qsort函数

我们之前写的冒泡排序只能排序整型数组,

而qsort是C语言提供的一个库函数,

可以排序任意类型的数据。

这里给出c++官网的qsort函数说明:

C语言进阶篇 第二讲【回调函数之模拟实现qsort函数】_第1张图片

C语言进阶篇 第二讲【回调函数之模拟实现qsort函数】_第2张图片

通过官网的说明,我们可以发现:

在qsort函数里,我们还调用了一个函数——compar().

也就是说,我们是在某一个函数里,通过一个函数的地址去访问了这个新的函数,

那么在此处,compar()函数就是我们上一节学习的回调函数。 

 

qsort对比于我们之前写的冒泡排序的优势在于,他可以排任何数据类型的序。

比如,我们创建一个结构体,

struct Stu

{

char name[20];

int age;

}

struct Stu s[3] = {{"zhangsan",20},{"lisi",21},{"wangwu",22}};

我们想对这个学生列表,以年龄的大小排序,

这时候,整型的冒泡排序就做不到了,

同理,整型的冒泡排序也无法排浮点型的数组。

所以,我们需要借助qsort函数,一个更通用的函数,来排序。 

注:qsort函数是借助快速排序算法来实现的,这里我们使用冒泡排序来改编。

 

二、函数实现

这是C库函数提供的qsort的参数和返回类型:

C语言进阶篇 第二讲【回调函数之模拟实现qsort函数】_第3张图片

void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));

分别是:

1个void的指针,

2个size_t的参数,

1个函数指针。

base:目标数组的起始位置   ------   arr

num:数组的元素个数   ------   sizeof(arr)/sizeof(arr[0])

width:元素大小单位   ------   sizeof(arr[0])

compar:比较函数

我们首先来分析一下这个函数:

int int_cmp(const void * p1, const void * p2)

因为每个数据的类型是不一样的,

所以我们可以封装一个比较函数,

把地址传进排序函数,

到时候用指针去调用所指向的函数即可。

void*是什么?

无类型指针。

先测量一下他的大小:

    printf("%d\n", sizeof(void*));

结果是4.

再看看他能存放哪些数据的地址:

C语言进阶篇 第二讲【回调函数之模拟实现qsort函数】_第4张图片

同理,可以存放任何数据类型的地址。

但是,如果在这里,对pa进行解引用:*pa,

就会报错,

 同理,void类型的指针不能进行+1运算,

原因在于:你并不知道这个指针要访问几个字节。

综上,

void*不能解引用,不能++。

 现在我们回到这个比较函数。

int int_cmp(const void * p1, const void * p2),

这个函数的两个参数都是空指针,不知道访问几个字节,

但是我们需要比较他们的数据大小,需要解引用,怎么办?

强制类型转换即可。

(int*)e1,(int*)e2.

然后再解引用:

*(int*)e1,*(int*)e2.

做差:

*(int*)e1-*(int*)e2

作为返回值。

这个函数会:

当第一个元素较大时,返回正数;

当两个元素相等时,返回0;

当第二个元素较大时,返回负数。

现在我们演示一下qsort函数的使用:

#include 
//qsort函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

qsort的使用方法:

第一个参数:待排序数组的首元素地址

第二个参数:待排序数组的元素个数

第三个参数:待排序数组的每个元素的大小-单位是字节

第四个参数:是函数指针,比较两个元素的所用函数的地址,这个函数由使用者自己实现

函数指针的两个参数是:待比较的两个元素的地址

#include 
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
void _swap(void *p1, void * p2, int size)
{
    int i = 0;
    for (i = 0; i< size; i++)
   {
        char tmp = *((char *)p1 + i);
*(( char *)p1 + i) = *((char *) p2 + i);
       *(( char *)p2 + i) = tmp;
   }
}
void bubble(void *base, int count , int size, int(*cmp )(const void *,const void *))
{
    int i = 0;
    int j = 0;
    for (i = 0; i< count - 1; i++)
   {
       for (j = 0; j 0)
           {
               _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
           }
       }
   }
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    //char *arr[] = {"aaaa","dddd","cccc","bbbb"};
    int i = 0;
    bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

你可能感兴趣的:(C语言)