当前位置: 首页 > article >正文

零基础自学C语言|深入理解指针 ④

📌回调函数

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

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

在上一节我们写的计算机的实现的代码中,有的代码是重复出现的,其中虽然执行计算的逻辑是区别的,但是输入输出操作是冗余的,有没有办法,简化一些呢?

因为红色框中的代码,只有调用函数的逻辑是有差异的,我们可以把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这里其实使用的就是回调函数的功能。

修改前:

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf("*****************************\n");
	printf("*****************************\n");
	printf("****  1.加法     2.减法  ****\n");
	printf("****  3.乘法     4.除法  ****\n");
	printf("****  0.退出   **************\n");
	printf("*****************************\n");
}



int main()
{
	int a = 0;
	int x = 0, y = 0;
	do
	{
		menu();
		printf("请输入数字进行计算->\n");

		scanf("%d", &a);
		switch (a)
		{
		case 1:
			printf("输入操作数\n");
			scanf("%d%d", &x, &y);
			printf("结果为:%d\n", Add(x, y));
			break;
		case 2:
			printf("输入操作数\n");
			scanf("%d%d", &x, &y);
			printf("结果为:%d\n", Sub(x, y));
			break;
		case 3:
			printf("输入操作数\n");
			scanf("%d%d", &x, &y);
			printf("结果为:%d\n", Mul(x, y));
			break;
		case 4:
			printf("输入操作数\n");
			scanf("%d%d", &x, &y);
			printf("结果为:%d\n", Div(x, y));
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (a);

	return 0;
}


修改后:

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf("*****************************\n");
	printf("*****************************\n");
	printf("****  1.加法     2.减法  ****\n");
	printf("****  3.乘法     4.除法  ****\n");
	printf("****  0.退出   **************\n");
	printf("*****************************\n");
}



void Cal(int(*pf)(int, int))
{
	int x = 0, y = 0;
	printf("输入操作数\n");
	scanf("%d%d", &x, &y);
	printf("结果为:%d\n", pf(x, y));
}

int main()
{
	int a = 0;
	do
	{
		menu();
		printf("请输入数字进行计算->\n");

		scanf("%d", &a);
		switch (a)
		{
		case 1:
			Cal(Add);
			break;
		case 2:
			Cal(Sub);
			break;
		case 3:
			Cal(Mul);
			break;
		case 4:
			Cal(Div);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (a);

	return 0;
}

📌qsort的使用

qsort是一种用来排序的库函数,头文件是<stdlib.h>。相比冒泡排序,它的好处是可以排序任意数据类型,而冒泡排序只能排序整型。它的底层代码使用的是快速排序。

qsort函数一共有四个参数

  1. 指针参数,代表首元素的地址
  2. 整型参数,代表要排序的元素的个数
  3. 整型参数,代表要排序的元素的大小
  4. 函数指针参数,提供排序规则,当传入两个元素时,保证:(1)两个元素相等,返回0。(2)两个元素顺序反了,返回负值。(3)两个元素顺序正确,返回正值。

光说无用,我们举一个简单的例子。

📍qsort排序整形:

int int_cmp(const void* p1, const void* p2)//排序规则
{
	return(*(int*)p1 - *(int*)p2);
}

int main()
{
	int arr[] = { 5,9,1,3,0,7,6,4,2,8 };
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), int_cmp);//qsort函数

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	return 0;
}

📍qsort排序结构体数据

了解了qsort的基本用法后,我们就可以做一些冒泡排序做不到的事:

struct stu//学生
{
	char name[20];//名字
	int age;//年龄
};

struct stu s[] = { {"zhangsan",38},{"lisi",40},{"wangwu",18} };

int cmp_stu_by_name(const void* p1, const void* p2)//名字排序的方法
{
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}

int cmp_stu_by_age(const void* p1, const void* p2)//年龄排序的方法
{
	return ((struct stu*)p1)->age - ((struct stu*)p2)->age;

}

void test1()//年龄排序
{
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}

void test2()//名字排序
{
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);

}


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

值得注意的是,名字排序的方法中,用到了一个新的函数:strcmp

这个函数是用来排序字符串的 

这个函数需要传入两个字符串参数

它根据两个字符串第一个不同的相同位的ASCII码值来排序,例如“zhangsan”和“lisi”的第一位分别是“z”和“l”而这两个字符不相同,则根据他们的ASCII码值来排序,如果第一位相同,就找第二位,以此类推。

年龄排序:

名字排序: 

📌qsort函数的模拟实现

学习了qsort函数的基本逻辑以后,我们是否能利用冒泡排序写出一个自己的qsort函数呢?

答案是可以的。

下面是参考代码,并附上int类型排序的测试:

int int_cmp(const void* p1, const void* p2)//int类型排序规则
{
	return *(int*)p1 - *(int*)p2;
}

void _cmp(char* px, char* py, int size)//交换函数
{
	for (int i = 0; i < size; i++)
	{
		char cmp = 0;
		cmp = *px;
		*px = *py;
		*py = cmp;
		px++;
		py++;
	}

}

void my_bubble(void* base, int count, int size, int (*cmp)(const void* p1, const void* p2))//排序函数
{
	for (int i = 0; i < count - 1; i++)
	{
		for (int j = 0; j < count - i - 1; j++)
		{
			//利用char类型的指针只有一个字节,交换每个字节上的数字,实现将任意大小的数值交换
			if (cmp((char*)base+j*size, (char*)base+(j+1)*size) > 0)
			{
				_cmp((char*)base + j * size, (char*)base + (j + 1) * size, size);//将交换的函数封装
			}
		}
	}

}





int main()//函数测试
{
	int arr[] = { 3,6,7,9,1,2,0,4,5,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	my_bubble(arr, sz, sizeof(arr[0]), int_cmp);
	
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	return 0;
}

排序测试结果:


http://www.kler.cn/news/161670.html

相关文章:

  • 2002-2021年全国各地级市环境规制18个相关指标数据
  • vue项目中如何引入zip压缩包之解决方案
  • html和css写去哪儿导航条
  • CefSharp 获取POST(AJAX)、GET消息返回值(request)
  • “低代码开发:快餐大厨还是魔术棒?探寻软件开发的诙谐世界“
  • ZooKeeper学习一
  • Verilog学习 | 用initial语句写出固定的波形
  • 《使用ThinkPHP6开发项目》 - 安装ThinkPHP框架
  • Amazon CodeWhisperer 正式发布可免费供个人使用
  • 【Flink系列五】Checkpoint及Barrier原理
  • CCF刷题记录 -- 202305-2:矩阵运算 --python解法
  • 【每日一题】—— D. Divide and Equalize(Codeforces Round 903 (Div. 3))(数学、数论)
  • 12.07
  • Hadoop学习笔记(HDP)-Part.19 安装Kafka
  • Win10 安装.NET Framework 3.5 报错0x80240438
  • 利用 Python 进行数据分析实验(四)
  • log4j日志框架的使用
  • 【redis笔记】分布式锁
  • 在 CentOS 或 Red Hat 系统上安装 Citus 组件
  • Gateway
  • Hive增强的聚合、多维数据集、分组和汇总
  • 动手学深度学习——Anaconda、pytorch、paddle安装(cpu版本)
  • Python-封装配置文件
  • 学习-ES
  • 三层交换机配置DHCP服务
  • 在vue中深度选择器的使用
  • 什么是css初始化
  • 代客泊车手势召车功能设计规范
  • 【计算机网络学习之路】HTTP响应报文Cookie原理
  • 玩转Sass:掌握数据类型!