程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<7>

大家好啊,我是小象٩(๑òωó๑)۶
我的博客:Xiao Xiangζั͡ޓއއ
很高兴见到大家,希望能够和大家一起交流学习,共同进步

今天我们一起来学习转移表,回调函数,qsort...

目录

  • 一、转移表
    • [1.1 定义与原理](#1.1 定义与原理)
    • [1.3 优点](#1.3 优点)
  • 二、回调函数是什么?
    • [1.1 原理](#1.1 原理)
    • [1.2 使用方法](#1.2 使用方法)
    • [1.3 优点](#1.3 优点)
  • [三、qsort 使用举例](#三、qsort 使用举例)
    • [3.1 使用qsort函数排序整型数据](#3.1 使用qsort函数排序整型数据)
    • [3.2 使用qsort排序结构数据](#3.2 使用qsort排序结构数据)
  • 四、结尾

一、转移表

在 C 语言中,转移表(Jump Table)是一种用于实现多路分支选择的技术,也被称为跳转表或分支表。

1.1 定义与原理

转移表本质上是一个函数指针数组,数组中的每个元素都是一个指向函数的指针。它的原理是通过计算索引值来选择调用数组中的某个函数指针,从而实现根据不同条件跳转到不同代码段的功能,类似于根据索引查找表中的内容并执行相应操作。

1.3 优点

代码清晰简洁:

相比于使用大量的if-else语句或switch语句嵌套,转移表能使代码结构更加清晰,逻辑更加直观。尤其是在处理多个分支情况时,代码的可读性更高。

高效性:

在执行多路分支选择时,转移表的查找和跳转操作通常比复杂的if-else或switch判断更快。因为它主要通过数组索引直接定位到目标函数,而不需要逐个进行条件判断。

可扩展性:

当需要添加新的分支或功能时,只需在转移表中添加相应的函数指针,并实现对应的函数逻辑即可,对原有代码的改动较小,易于维护和扩展。

举例:计算器的⼀般实现:

c 复制代码
#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("*************************\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;
}

使⽤函数指针数组的实现:

c 复制代码
#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;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}

	} while (input);
	return 0;
}

二、回调函数是什么?

回调函数是一个作为参数传递给另一个函数的函数,而接收回调函数作为参数的函数会在合适的时候调用这个传递进来的函数。简单来说,就是 A 函数将 B 函数作为参数传递给 C 函数,C 函数在某个时刻调用 B 函数,此时 B 函数就是回调函数。

1.1 原理

回调函数的核心原理基于函数指针。在 C 语言中,函数名本质上代表该函数的入口地址,而函数指针则可以存储这个地址。通过将函数指针作为参数传递给另一个函数,接收该参数的函数就可以在需要的时候通过这个指针调用对应的函数。

1.2 使用方法

以下是使用回调函数的基本步骤:

定义回调函数:首先要定义一个符合特定参数和返回值要求的函数,这个函数将作为回调函数使用。

定义接收回调函数作为参数的函数:该函数需要有一个函数指针类型的参数,用于接收回调函数的地址。

调用接收回调函数的函数:在调用时,将回调函数的名称作为参数传递给接收回调函数的函数。

1.3 优点

灵活性:通过回调函数,可以在不修改接收回调函数的函数的源代码的情况下,改变其行为。只需要传递不同的回调函数,就可以实现不同的功能。

代码复用:可以将一些通用的逻辑封装在接收回调函数的函数中,而将具体的处理逻辑放在回调函数中。这样,不同的回调函数可以复用接收回调函数的函数的代码。

异步编程:在异步编程中,回调函数非常有用。当某个操作完成时,可以通过回调函数通知调用者进行后续处理。

c 复制代码
//使⽤回调函数改造前
#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("*************************
						\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;

三、qsort 使用举例

qsort 是 C 标准库中用于对数组进行快速排序的函数,它基于快速排序算法实现,能对任意类型的数组进行排序。

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

c 复制代码
#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
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;

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

3.2 使用qsort排序结构数据

c 复制代码
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;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
	struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
	struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
	test2();
	test3();
	return 0;
}

注意事项

比较函数的实现:比较函数的实现要正确,确保能准确反映元素之间的大小关系。不同的比较函数实现可以实现升序、降序或其他自定义的排序规则。

类型转换:在比较函数中,由于 qsort 函数的通用性,传入的参数是 const void *

类型,需要将其转换为实际的数据类型才能进行比较操作。 稳定性:qsort

是不稳定的排序算法,即相等元素的相对顺序在排序后可能会改变。如果需要稳定的排序算法,可以考虑使用其他排序函数或自行实现稳定排序。

性能:qsort 平均时间复杂度为 (O(n log n)),但在最坏情况下时间复杂度为

(O(n^2))。不过在大多数实际应用场景中,它的性能表现良好。

四、结尾

这一课的内容就到这里了,下节课继续学习指针的其他一些知识
如果内容有什么问题的话欢迎指正,有什么问题也可以问我!

相关推荐
suuijbd40 分钟前
单例模式和单例Bean
java·开发语言·单例模式
华梦岚42 分钟前
Perl语言的语法糖
开发语言·后端·golang
厉君韵1 小时前
Scala语言的数据类型
开发语言·后端·golang
霜雪殇璃2 小时前
2025.1.8(qt图形化界面之消息框)
开发语言·qt
runscript.sh3 小时前
golang 开启HTTP代理认证
开发语言·http·golang
一念&4 小时前
python爬虫--简单登录
开发语言·爬虫·python
苏貝貝4 小时前
【C++】多态(下)
java·开发语言·c++
明月看潮生4 小时前
青少年编程与数学 02-008 Pyhon语言编程基础 25课题、文件操作
开发语言·python·青少年编程·文件操作·编程与数学
亲爱的老吉先森5 小时前
C语言练习题
c语言·数据结构·算法
佐咖5 小时前
Qt中的绘图事件和绘图设置(详细图文教程_附代码)
开发语言·c++·qt·用户界面