回调函数概念

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一
个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该
函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。
简单的来说,回调函数就是通过函数指针调用的函数就是回调函数。我们库里面有一个函数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 ) 这样一个函数。再看下图:

如果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;}

运行结果:


需要主要的是,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就实现完成了,我们来看一下运行结果:

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

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