深入理解指针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节示例中的比较函数们也都是回调函数。

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

相关推荐
祈安_2 天前
C语言内存函数
c语言·后端
norlan_jame3 天前
C-PHY与D-PHY差异
c语言·开发语言
czy87874753 天前
除了结构体之外,C语言中还有哪些其他方式可以模拟C++的面向对象编程特性
c语言
m0_531237173 天前
C语言-数组练习进阶
c语言·开发语言·算法
Z9fish3 天前
sse哈工大C语言编程练习23
c语言·数据结构·算法
代码无bug抓狂人3 天前
C语言之单词方阵——深搜(很好的深搜例题)
c语言·开发语言·算法·深度优先
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习
枫叶丹43 天前
【Qt开发】Qt界面优化(七)-> Qt样式表(QSS) 样式属性
c语言·开发语言·c++·qt
with-the-flow4 天前
从数学底层的底层原理来讲 random 的函数是怎么实现的
c语言·python·算法
Sunsets_Red4 天前
P8277 [USACO22OPEN] Up Down Subsequence P 题解
c语言·c++·算法·c#·学习方法·洛谷·信息学竞赛