[C]基础12.深入理解指针(4)

  • 博客主页:向不悔
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。

文章目录


0、总结

1、回调函数是什么?

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

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

简单画流程图理解:

  • 主函数将回调函数指针作为参数传递给调用函数(注册回调)
  • 调用函数执行自身逻辑(如监听事件/等待条件)
  • 当特定事件/条件被触发时,调用函数通过保存的指针调用对应的回调函数
  • 回调函数执行具体响应逻辑,处理完成后流程继续

看简单的回调函数代码,加深理解:

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

// 定义函数指针类型
typedef void (*CallbackFunc)(int);

// 接收函数指针作为参数的函数
void event_handler(CallbackFunc callback) {
    int event_id = 42;
    callback(event_id); // 调用回调函数
}

// 自定义回调函数(需与 CallbackFunc 类型匹配)
void my_callback(int event_id) {
    printf("Event %d triggered!\n", event_id);
}

int main() {
    event_handler(my_callback); // 传递函数名(即函数地址)
    return 0;
}
c 复制代码
运行:
Event 42 triggered!

先理解主调函数、被调函数、回调函数这三个概念:

  • 主调函数(调用者) :在调用event_handler时,main是主调函数;在event_handler调用my_callback时,event_handler成为主调函数。
  • 被调函数event_handlermain调用,因此是被调函数;而my_callbackevent_handler调用,也是被调函数。
  • 回调函数my_callback作为参数传递给event_handler,并在其中被调用,因此是回调函数。

然后理解步骤:

  • 1、mainmy_callback的函数地址作为参数传递给event_handler
  • 2、event_handler生成事件ID(硬编码为42)
  • 3、通过函数指针调用my_callback,并传递事件ID
  • 4、回调函数执行具体的业务逻辑(打印事件信息)

2、改善代码

2.1 使用回调函数改造前

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>  
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}
int mul(int a, int b)
{
    return a * b;
}
int div(int a, int b)
{
    return a / b;
}

int main()
{
    int x, y;
    int input = 1;
    int ret = 0;
    do {
        printf("********************\n");
        printf(" 1:add       2:sub\n");
        printf(" 3:mul       4:div\n");
        printf("****** 0.exit ******\n");
        printf("请选择: ");
        scanf("%d", &input);
        switch (input)
        {
            case 1:
                printf("输入操作数:");
                scanf("%d %d", &x, &y);
                ret = add(x, y);
                printf("ret = %d\n", ret);
                break;
            case 2:
                printf("输入操作数:");
                scanf("%d %d", &x, &y);
                ret = sub(x, y);
                printf("ret = %d\n", ret);
                break;
            case 3:
                printf("输入操作数:");
                scanf("%d %d", &x, &y);
                ret = mul(x, y);
                printf("ret = %d\n", ret);
                break;
            case 4:
                printf("输入操作数:");
                scanf("%d %d", &x, &y);
                ret = div(x, y);
                printf("ret = %d\n", ret);
                break;
            case 0:
                printf("退出程序\n");
                break;
            default:
                printf("选择错误\n");
                break;
        }
    } while (input);
    return 0;
}

2.2 使用回调函数改造后

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>  
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
void calc(int (*pf)(int, int))
{
	int x, y, t;
	printf("输入操作数:");
	scanf("%d %d", &x, &y);
	t = pf(x, y);
	printf("ret = %d\n", t);
}
int main()
{
	int input = 1;
	int ret = 0;
	do {
		printf("********************\n");
		printf(" 1:add       2:sub\n");
		printf(" 3:mul       4:div\n");
		printf("****** 0.exit ******\n");
		printf("请选择: ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

3、qsort使用举例

3.1 使用qsort函数排序整型数据

c 复制代码
#include <stdio.h> /* printf */
#include <stdlib.h>  /* qsort */

// 回调函数
// const 保证数据的只读性
int int_cmp(const void *p1, const void *p2)
{
	return (*(int*)p1 - *(int*)p2);
}

int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	// main作为主调函数,把回调函数的地址传参给调用qsort函数。
	// qsort作为main的被调函数,去调用回调函数(int_cmp)
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}
c 复制代码
运行:
0 1 2 3 4 5 6 7 8 9

3.2 使用qsort排序结构数据

c 复制代码
#include <stdio.h> /* printf */
#include <stdlib.h>  /* qsort */
struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
// 按照年龄来排序
void test1()
{
	struct Stu s[] = { {"zhangsan", 20}, {"lisi",30}, {"wangwu", 10} };
	int sz = sizeof(s) / sizeof(s[0]);
	printf("\n");
	printf("年龄排序前:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	printf("\n");
	printf("年龄排序后:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
}
// 按照名字来排序
void test2()
{
	struct Stu s[] = { {"zhangsan", 20}, {"lisi",30}, {"wangwu", 10} };
	int sz = sizeof(s) / sizeof(s[0]);
	printf("\n");
	printf("名字排序前:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	printf("\n");
	printf("名字排序后:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test1();
	test2();
}
c 复制代码
运行:

年龄排序前:
20 30 10
年龄排序后:
10 20 30
名字排序前:
zhangsan lisi wangwu
名字排序后:
lisi wangwu zhangsan

4、qsort函数的模拟实现

使用回调函数,模拟实现qsort(采用冒泡的方式)。

注意:在这里,第一次使用void*的指针。

c 复制代码
#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

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

void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; 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[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}
c 复制代码
运行:
0 1 2 3 4 5 6 7 8 9

完。

相关推荐
祁同伟.3 小时前
【数据结构 · 初阶】- 堆的实现
c语言·数据结构
夜夜敲码3 小时前
C语言教程(十六): C 语言字符串详解
c语言·开发语言
宋康3 小时前
C语言结构体和union内存对齐
c语言·开发语言
学习噢学个屁3 小时前
基于51单片机的超声波液位测量与控制系统
c语言·单片机·嵌入式硬件·51单片机
Cao1234567893214 小时前
简易学生成绩管理系统(C语言)
c语言·开发语言
Yurko134 小时前
【C语言】全局变量、静态本地变量
c语言·学习
Yhame.5 小时前
【使用层次序列构建二叉树(数据结构C)】
c语言·开发语言·数据结构
杜小暑7 小时前
动态内存管理
c语言·开发语言·动态内存管理
YuforiaCode7 小时前
第十二届蓝桥杯 2021 C/C++组 直线
c语言·c++·蓝桥杯