【C】回调函数和qsort详解

简介: 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

回调函数概念

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一

个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该

函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或

条件进行响应。

简单的来说,回调函数就是通过函数指针调用的函数就是回调函数。我们库里面有一个函数qsort,就用到了回调函数。


库函数qsort(在头文件stdlib.h中)

这个函数是一个排序函数,我们学过冒泡排序,但是那个排序能够排序整型,我们今天说的qsort可以排序任何类型,它默认排的是升序。我们来看一下他的参数列表:


void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );


这里的void* 为无类型,因为可以排序任意类型的数组,所以不知道你的是哪种数组的指针用void* 来接收,size_t为无符号整型,num表示的是数组的大小,width为数组中每个元素多大,最后一个参数就是函数指针了,指向的是 int xxxx(const void *elem1, const void *elem2 ) 这样一个函数。再看下图:

1be1116849574940bc613f67ab203f22.png

如果elem1指向的内容大于elem2指向的内容就返回大于0的数字,如果等于就返回0,如果小于就返回小于0的数字。

那么接下来我们就上手来用下这个函数:

// qsort需要的函数
int sort_int(const void* p1, const void* p2)
{
  return *((int*)p1) - *((int*)p2);
}
// 打印函数
void print(int* arr, int size)
{
  for (int i = 0; i < size; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
int main()
{
  int arr[] = { 5,6,4,8,1,3,2,7,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
  qsort(arr, sz, sizeof(arr[0]), sort_int);
  print(arr, sz);
  return 0;
}

运行结果:

c50f6721b3604731aa35fa8339a1ff39.png

需要主要的是,void* 不能直接对它解引用操作,需要先强制类型转换,然后再操作。


qsort模拟实现

因为我们目前就学过冒泡排序,所以今天我们就用冒泡排序来模拟实现qsort.

我们将上面的代码拿下来,把qsort改为my_qsort.

int sort_int(const void* p1, const void* p2)
{
  return *((int*)p1) - *((int*)p2);
}
// 打印函数
void print(int* arr, int size)
{
  for (int i = 0; i < size; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
int main()
{
  int arr[] = { 5,6,4,8,1,3,2,7,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
  my_qsort(arr, sz, sizeof(arr[0]), sort_int);
  print(arr, sz);
  return 0;
}

那现在我们只需要实现my_qsort就可以了,我们这里传递的是整型数组,所以可以用整型指针来接受,但是我们qsort是可以排序任何类型的数据的,所以我们要用void* 来接受。所以函数的参数列表为:


void my_qsort(void* arr, size_t num, size_t width, int (cmp)(const void, const void*))

我们冒泡排序不管排什么数据,排的趟数肯定不会变的,而且每一趟比较的对数也是固定的,所以我们可以把冒泡排序的大体框架实现出来,这里的函数指针指向的函数就可以认为比较两个元素的大小的函数。我们的比较函数需要两个指针,也就是两个元素的地址,这时候我们传过来的元素大小就起到作用了,我们首元素的地址可以认为就是arr,第二个就是arr+1width,又因为只有arr是(char)类型是,我们才能实现每次跳的单位是1,所以我们在这之前要将arr强制转化为(char*)类型因为我们拍的元素是变化的,我们这里将参数设置为

(char*)arr+j* width,(char*)arr+(j+1)*width,这样我们就可以实现my_sort了。

void my_qsort(void* arr, size_t num, size_t width, int (*cmp)(const void*, const void*))
{
  for (unsigned int i = 0; i < num - 1; i++)
  {
    for (unsigned int j = 0; j < num - 1 - i; j++)
    {
      if (cmp((char*)(arr)+j*width,(char*)(arr)+(j+1)*width)>0)
      {
        Swap((char*)(arr) + j * width, (char*)(arr) + (j + 1) * width,width);
      }
    }
  }
}

这里就剩下一个Swap函数了,这个函数实现以后,我们的my_qsort就可以正常工作了。

因为我们还是要交换两个元素,所以我们要将两个元素的地址传过去,传过去的为(char*)类型的所以我们用char* 来接收就可以,又因为char*一次只能访问一个字节,如果我们要交换整型的话只需要将4个字节的内容都交换就可以了,所以我们还需要讲元素的大小过去。

void Swap(char* p1, char* p2, int width)
{
  char tmp = 0;
  for (int i = 0; i < width; i++)
  {
    tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
    p1++;
    p2++;
  }
}

到这里我们的qsort就实现完成了,我们来看一下运行结果:

40c11f62441a4b5598b033ae2eeb3a9f.png

我们可以看到我们的排序也没有问题。


今天的分享就到这里结束了,感谢大家的关注和支持!

相关文章
|
2月前
|
存储 Python
有效的函数(一)
有效的函数(一)
|
6月前
|
程序员 C语言 Python
函数—C(上)
函数—C(上)
52 0
|
6月前
函数(三)
函数(三)
44 0
|
11月前
|
机器学习/深度学习
函数的使用
任务1 统计小组一门课程的总分及平均分。
58 1
|
编译器 C语言
C 中的函数
C 中的函数
|
算法 程序员 编译器
最简单的函数,看看就会了
最简单的函数,看看就会了
|
监控 程序员 C语言
|
程序员 C语言
|
C语言
可变长参数函数
可变长参数函数
134 0