深入理解指针Part5——回调函数及应用

1 回调函数的定义

回调函数就是一个通过函数指针调用的函数

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

概念有些抽象,接下来通过学习qsort函数以及其模拟实现来促进理解。

2 qsort函数

qsort函数是用快速排序方法能对数组、结构体等按提供的规则进行排序的一个库函数。

c 复制代码
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

该函数包含四个参数:

  • base是指向待排序内容的首元素地址;
  • num是待排序内容的元素个数;
  • size是待排序内容的一个元素大小,单位字节;
  • 函数指针compar是指向比较函数,需要自行编写,实现对待排序内容中两个元素的比较。

因为qsort函数并不知道你会传入一个什么样的数据类型,所以就需要你自己先写好一个比较函数,再传给qsort使用。比较函数的类型、参数与返回值都有规定,见参考链接。

qsort函数更详细说明的参考链接:legacy.cplusplus.com/reference/c...

下面是一个使用qsort函数排序整型数组的示例:

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
//const在*左侧,确保指针指向的内容不会被修改
{
	return *((int*)e1) - *((int*)e2);
    //要排序的是整形数组,元素是整形,这里先将void*类型强转成int*类型
    //强转成与元素对应的指针类型,解引用才能得到正确的结果
    //这里return返回相减的差,正好符合比较函数的返回值要求
}
int main()
{
	int arr[] = { 5,6,4,1,8,9,2,3,7 };
	int num = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, num, sizeof(arr[0]), cmp_int);
}

下面是一个使用qsort函数排序结构体数组的示例:

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

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

void test3()//这里展示了两种访问结构体指针指向内容的方法
{
	struct Stu s = { "cuihua", 18 };
	struct Stu* ps = &s;//结构体指针变量
	printf("%s\n", (*ps).name);
	printf("%s\n", ps->name);
	//结构体变量.成员名
	//结构体指针变量->成员名
}

//按照名字比较大小
//e1是指向一个结构体数据的,e1是指向另外一个结构体数据的
//名字是字符串,字符串的比较使用strcmp
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp((*(struct Stu*)e1).name, (*(struct Stu*)e2).name);
}

int cmp_stu_by_name1(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

//按照年龄来比较
int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

//测试qsort函数排序结构体数据 - 按名字比较
void test2()
{
	struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
	//{"lisi", 35}, {"wangwu", 12},{"zhangsan", 18}
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

//测试qsort函数排序结构体数据 - 按年龄比较
void test4()
{
	struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
	//{{"wangwu", 12}, "zhangsan", 18},{"lisi", 35}
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

int main()
{
	test2();
	test4();
	return 0;
}

3 模拟实现

第2节解释了qsort函数的参数及运行方式,接下来尝试模拟实现。这里排序方法采用更熟悉的冒泡排序,而非qsort的快速排序。

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>

void Swap(char* buf1, char* buf2, size_t sz)
{
	for (int i = 0; i < sz; i++)
	{
		int tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, size_t n, size_t sz, int (*cmp)(const void*, const void*))
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (cmp((char*)base + sz * j, (char*)base + sz * (j + 1)) > 0)
			{
				Swap((char*)base + sz * j, (char*)base + sz * (j + 1), sz);
			}
		}
	}
}

int cmp_int(const void* e1, const void* e2)
{
	return *((int*)e1) - *((int*)e2);
}

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

观察上述的示例,并回忆回调函数的定义。

回调函数就是一个通过函数指针调用的函数

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数

比较函数 cmp_int通过函数指针的形式传入bubble_sort函数并在其内部被调用,比较函数cmp_int就是一个回调函数。其实第2节示例中的比较函数们也都是回调函数。

可见,回调函数在实现一些复杂功能时具有独到的优势。想想如果不用回调函数应该是不太好实现相同的功能的。

相关推荐
日拱一卒——功不唐捐5 小时前
字符串匹配:暴力法和KMP算法(C语言)
c语言·算法
lcj25115 小时前
深入理解指针(4):qsort 函数 & 通过冒泡排序实现
c语言·数据结构·算法
4311媒体网6 小时前
C语言操作符全解析 C语言操作符详解
java·c语言·jvm
二年级程序员6 小时前
一篇文章掌握“顺序表”
c语言·数据结构
傻乐u兔7 小时前
C语言进阶————指针4
c语言·开发语言
历程里程碑7 小时前
Linux22 文件系统
linux·运维·c语言·开发语言·数据结构·c++·算法
2601_9491465315 小时前
C语言语音通知接口接入教程:如何使用C语言直接调用语音预警API
c语言·开发语言
知南x17 小时前
【Ascend C系列课程(高级)】(1) 算子调试+调优
c语言·开发语言
2的n次方_18 小时前
Runtime 执行提交机制:NPU 硬件队列的管理与任务原子化下发
c语言·开发语言