c语言之回调函数

1、回调函数的定义

什么是回调函数呢?

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

类似这段代码
在这里插入图片描述
不难看出,这是实现计算器的一段代码,先是定义了加减乘除这四个函数,在主函数中并没有去直接使用它,因为这四个函数的类型都一样,所以可以通过calc函数,形参用函数指针来接收这四个函数的地址,间接调用这四个函数。通过依赖函数指针,来间接调用函数,calc函数这种就称为回调函数。

2.回调函数的一个实例(qsort函数)

(1)qsort函数的介绍和使用

qsort函数是一个库函数,底层使用的是一个快速排序的方式,对数据进行排序,可以是任何的数据类型,这个函数可以直接使用
注意:这里的排序是按照升序的,
语法:

//qsort 底层使用的快速排序
void qsort( void* base, //待排序数据的起始地址
               size_t num,   //待排序数据的元素个数
	           size_t size,  
	        //待排序数据的一个元素的大小,单位是字节
	        int (*compar)(const void*, const void*)
//函数指针 - 指向了一个比较函数,这个函数是用来比较2个元素的
         );

例1: 对整型数组进行比较

#include <stdlib.h>
#include <stdio.h>
void print(int arr[], int sz)
{
   for (int i = 0; i < sz; i++)
   {
   	printf("%d ", arr[i]);
   }
}
int cmp_int(const void* e1, const void* e2)
{
   return *(int*)e1 - *(int*)e2;
}
void test1()
{
   int arr[10] = { 5,2,1,4,6,7,8,9,3,10, };
   int sz = sizeof(arr) / sizeof(arr[0]);
   qsort(arr, sz,sizeof(arr[0]), cmp_int);
   //使用的时候需要头文件#include<stdlib.h>
   print(arr,sz);
}
int main()
{
 test1();
}

这里其实需要注意的就是比较函数,为什么回调函数能实现任何类型数据的比较呢,其中很大一部分原因在于能够将两个数的比较方法剥离出来,你就拿冒泡排序来说,他就只能够比较两个整型数据的大小。
具体是怎么剥离出来的呢,且听我一 一到来。通过观察这个函数的使用方式,你会发现待排序的起始地址是void类型的,这就表明可以接收任何类型的数据,而且将这个待排序的元素个数和元素大小都接收过来,就表明可以一个字节一一个字节的进行比较。最后不直接使用这个比较函数,而是使用一个函数指针将这个比较函数的地址接收,间接的调用了这个比较函数,所以被称之为回调函数。
需要注意的点是,这个比较函数只能返回0和1,就拿上述例子来说,
int cmp_int(const void
e1, const void* e2),形参用void接收,加const是因为防止指针变量被指向的内容被修改。因为void不能解引用,所以需要强制类型转化为int*
例2:

 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
typedef struct stu
{
	char name[20];
	int age;
}stu;
//比较结构体变量的方法
//按年龄比
int cmp_age(void* e1, void* e2)
{
	return ((stu*)e1)->age - ((stu*)e2)->age;
}
void test2()
{
	stu arr[] = { {"zhangsan",21},{"lishi",18},{"wangwu",20} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_age);
}
//按名字比
int cmp_name(void* e1, void* e2)
{
	return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
	//恰好stcmp函数的返回类型为0和1
}
void test3()
{
	stu arr[] = { {"zhangsan",21},{"lishi",18},{"wangwu",20} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_name);
}
int main()
{
	test2();
	test3();
	return 0;
}

(2)利用冒泡排序模拟实现一个qsort

1、冒泡排序

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)//趟数
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j ] = arr[j+1];
				arr[j+1] = temp;
			}
		}
	}
}

2、模拟实现
我们已经回顾了一下冒泡排序,如何在冒泡排序的基础上进行修改,变成实现qsort的底层逻辑。

void print(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void swap(char* buf1, char* buf2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base,size_t num,size_t size, 
              int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < num; i++)//趟数
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, 
			              (char*)base + (j + 1) * size) > 0)
			{
				//交换
			  swap((char*)base + j * size, 
			              (char*)base + (j + 1) * size, size);
			}
		}
	}

}

int main()
{
	int arr[10] = { 5,2,1,4,6,7,8,9,3,10, };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz,sizeof(arr[0]), cmp_int);
	print(arr,sz);
}

在冒泡排序里,两个for循环都不需要变。后面比较两个数据需要改变一下,因为不一定是整型,所以不能直接使用>或<,因此就需要去使用一个比较函数,所以先必须找到这两个元素的地址,然后通过交换函数来进行交换。
那么到底该如何去找到这两个元素呢,我们当然会想到从传过来的base指针变量来下手,我们前面说过void是不能直接解引用的,而是需要强制类型转化,那么到底是强制类型转化为int还是char还是等等类型,这个就需要我们好好考虑一下了。因为数据的类型是不断变化的,如果使用int就只能交换int类型的数据,访问四个字节的元素,显然这是极其不方便的,而且也违背了qsort函数的通用性,所以我们就想了一个办法,使用char类型,char访问一个字节,是单位最小的,既然是单位最小的,其他单位我们不就可以在他们类型的大小吗,你又会发现我们传进来就真有这个类型的大小。所以char)base + j * size和(char*)base + (j + 1) * size就是两个元素的地址,把他传入到cmp_int函数。
比较以后还需要去交换,再次把两个元素的地址传过去,一个字节一个字节的交换,实现排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值