C语言进阶——2指针(三)

6. 函数指针数组

6.1 什么是函数指针数组

类比指针数组
存放字符指针:char* arr1[10];
存放整型指针:int* arr2[5];
函数指针数组:存放函数指针的数组

写法:


#include<stdio.h>

int main()
{
	//函数指针数组
	int(*pf[5])(int, int); //pf和[]先结合,说明pf是数组,数组的内容是 int(*)(int int)类型的函数指针
	//指向函数指针数组的指针
	int(*(*ppf)[5])(int, int) = &pf;
	return 0;
}

6.2 函数指针数组的用途——转移表

6.2.1 实现一个简易版计算器(switch语句)

主要功能:实现两个数字的加减乘除

#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 manu()
{
	printf("====================\n");
	printf("====1:add   2:sub===\n");
	printf("====3.mul   4:div===\n");
	printf("====0:exit       ====\n");
	printf("===================\n");
}

int main()
{
	int input = 0;
	int x, y;
	int ret = 0;
	do
	{
		manu();
		printf("请输入您的选择:\n");
		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");
	  }
	} while (input);

	return 0;
}

在这里插入图片描述

分析,我们发现每个case语言都是类似的,如果功能更多,switch语句只会越来越多,实在是太繁琐了
这时候就用到我们的函数指针数组了

6.2.2 使用函数指针数组实现一个简易版的计算器——转移表

分析:
通过使用一个函数指针数组,大大简化了我们的代码,以后需要添加其他功能
只需要修改函数指针数组内容,还有具体功能实现,再就是在while中判断条件进行一个小修改
在这里插入图片描述

#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;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		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);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}


	return 0;
}

在这里插入图片描述

7. 指向函数指针数组的指针

7.1 什么是指向函数指针数组的指针

从函数指针数组去改写指向函数指针数组的指针,不容易出错。


#include<stdio.h>

int main()
{
	//函数指针数组
	int(*pf[5])(int, int); //pf和[]先结合,说明pf是数组,数组的内容是 int(*)(int int)类型的函数指针
	//指向函数指针数组的指针
	int(*(*ppf)[5])(int, int) = &pf; //ppf先和*结合,说明是指针,把(*ppf)取掉,就是我们指针指向的函数指针数组 int(*)[5](int,int)
	return 0;
}

分析
在这里插入图片描述

指向函数指针数组的指针的一个很重要的用途就是回调函数,下面介绍什么是回调函数

8. 回调函数

8.1 什么是回调函数

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

8.2 使用函数指针调用 回调函数 实现一个简易版的计算器

在6.2.1代码的基础上进行改进,构建一个函数,函数参数就是函数指针
分析
在这里插入图片描述

#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 manu()
{
	printf("====================\n");
	printf("====1:add  2:sub===\n");
	printf("====3.mul   4:div===\n");
	printf("====0:exit       ====\n");
	printf("===================\n");
}


void clc(int(*pf)(int,int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	int x, y;
	int ret = 0;
	do
	{
		manu();
		printf("请输入您的选择:\n");
		scanf("%d", &input);
	

	  switch(input)
	  {
	   case 1:
		   clc(add);
			break;
	   case 2:
		   clc(sub);
		   break;
	   case 3:
		   clc(mul);
		   break;
	   case 4:
		   clc(div);
		   break;
	   case 0:
		   printf("您已退出计算器\n");
		   break;
	   default:
		   printf("您的输入有误,请重新输入:\n");
		   break;
	  }
	} while (input);

	return 0;
}

在这里插入图片描述

8.3 结合8.2的实例我们再理解下我们的回调函数

在这里插入图片描述

8.4 回调函数的使用——库函数qsort

在我们的cplusplus中搜下我们的qsort

该函数一共有四个参数:
base:数组首元素的地址
num:数组的长度
size:在数组中每个元素的大小,int类型的大小就是siziof(int);
compar:一个比较函数
在这里插入图片描述

对于compar函数的返回值,这里也做了规定,
在这里插入图片描述

8.4.1 使用库函数qsort实现整形的排序

我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。

#include<stdio.h>
#include<stdlib.h>
int int_com(const void* p1, const void* p2) //这里的参数设置为void*是因为你不知道传过来的数据究竟是什么类型的,
//加const是因为我们只是对数据进行比较,不希望这里对数据数值进行一个修改,所以加了const
{
	return (*(int*)p1) - (*(int*)p2);
}


//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{   //使用qsort对数组进行排序,升序
	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_com);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	return 0;
}

在这里插入图片描述

8.4.2 使用库函数qsort实现结构体的排序


#include<stdio.h>
#include<stdlib.h>
struct student
{
	char name[20];//名字
	int age;//年龄
};

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


//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{   //使用qsort对结构体进行排序
	
	struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };

	qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);

	return 0;
}
  1. 先根据年龄排序

调试查看我们的结构体,排序前顺序
在这里插入图片描述
调试后
在这里插入图片描述

  1. 根据名字排序

根据名字排序,所以比较两个字符串,这里调用到了我们的字符串比较函数strcmp()

#include<stdio.h>
#include<stdlib.h>
struct student
{
	char name[20];//名字
	int age;//年龄
};

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



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


//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{   //使用qsort对结构体进行排序
	
	struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };

	//qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);
	qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byName);

	return 0;
}

调试查看调用函数之前前
在这里插入图片描述
调用函数之后
在这里插入图片描述

8.4.3 使用冒泡排序实现qsort函数的功能

1. 先实现我们的冒泡排序

思路:
在这里插入图片描述

代码实现

#include<stdio.h>

void bubbySort(int arr[],int size)
{
	int i = 0;
	for (i = 0; i < size - 1; i++)//本层循环控制比较躺数
	{
		int j = 0; 
		for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
		{
			if (arr[j] < arr[j + 1])
			{
				int tem = arr[j];
				arr[j] = arr[j+ 1];
				arr[j + 1] = tem;
			}
		}
	}
}

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

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

在这里插入图片描述

目前的冒泡排序只能实现int类型的排序,那我们想实现其他类型如何修改函数

2. 模仿qsort的功能实现一个通用的冒泡排序

修改冒泡排序函数,使其能够排序任意类型数据
仿照我们的sort函数去改造我们的冒泡排序
思路:传参我们和sort一样

关键点1:在每趟比较的时候,我们需要表示清楚我们进行比较的前一个元素和后一个元素
我们不清楚传进来的是什么类型的数据,可能的int可能是结构体,所以我们按字节大小来进行前后移动,来表示前一个和后一个,所以我们将传入的元素强制类型转换为(char*)类型
前一个元素:(char* )base + j * width, j初值是0 ,所以这里加j*width就是加0,表示首元素的地址
后一个元素:(char *)base + (j + 1) * width) ,这里就是我们的首元素地址+一个元素大小,这也是为什么我们第三个参数要传我们数据的大小的原因

关键点2:当我们判断,前一个元素小于后一个元素的时候就要进行交换
这里交换,我们不能直接写死,不能写int或者其他,但是不管它是什么类型,我们可以让它内部一个字节一个字节的交换,直到交换传入这个数据的一个大小(Width)

  1. 比较整形
//一个通用的冒泡排序

#include<stdio.h>


void swap(char* m, char* n, int width) //我们拿到相邻的两个元素,和大小,然后全部一个一个的交换
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tem = *m;
		*m = *n;
		*n = tem;
		m++;
		n++;
	}
}


void bubbySort(void* base,size_t size,size_t width,int(*cmp)(const void* e1,const void* e2))
{
	size_t i = 0;
	for (i = 0; i < size - 1; i++)//本层循环控制比较趟数
	{
		size_t j = 0;
		for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}


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


void test1()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	bubbySort(arr, size, sizeof(arr[0]), cmp_int);

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

int main()
{
	test1();//比较整形
	return 0;
}

在这里插入图片描述
2. 比较结构体

//一个通用的冒泡排序

#include<stdio.h>


void swap(char* m, char* n, int width) //我们拿到相邻的两个元素,和大小,然后全部一个一个的交换
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tem = *m;
		*m = *n;
		*n = tem;
		m++;
		n++;
	}
}


void bubbySort(void* base,size_t size,size_t width,int(*cmp)(const void* e1,const void* e2))
{
	size_t i = 0;
	for (i = 0; i < size - 1; i++)//本层循环控制比较趟数
	{
		size_t j = 0;
		for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}


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




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

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


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

void test1()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	bubbySort(arr, size, sizeof(arr[0]), cmp_int);

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



void test2()
{
	struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };

	bubbySort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);
	bubbySort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byName);
}

int main()
{
	//test1();//比较整形
	test2();//比较结构体
	return 0;
}

在这里插入图片描述
在这里插入图片描述

【顶级EI完整复现】【DRCC】考虑N-1准则的分布鲁棒机会约束低碳经济调度(Matlab代码实现)内容概要:本文介绍了名为《【顶级EI完整复现】【DRCC】考虑N-1准则的分布鲁棒机会约束低碳经济调度(Matlab代码实现)》的技术资源,聚焦于电力系统中低碳经济调度问题,结合N-1安全准则与分布鲁棒机会约束(DRCC)方法,提升调度模型在不确定性环境下的鲁棒性和可行性。该资源提供了完整的Matlab代码实现,涵盖建模、优化求解及仿真分析全过程,适用于复杂电力系统调度场景的科研复现与算法验证。文中还列举了大量相关领域的研究主题与代码资源,涉及智能优化算法、机器学习、电力系统管理、路径规划等多个方向,展示了广泛的科研应用支持能力。; 适合人群:具备一定电力系统、优化理论和Matlab编程基础的研究生、科研人员及从事能源调度、智能电网相关工作的工程师。; 使用场景及目标:①复现高水平期刊(如EI/SCI)关于低碳经济调度的研究成果;②深入理解N-1安全约束与分布鲁棒优化在电力调度中的建模方法;③开展含新能源接入的电力系统不确定性优化研究;④为科研项目、论文撰写或工程应用提供可运行的算法原型和技术支撑。; 阅读建议:建议读者结合文档提供的网盘资源,下载完整代码与案例数据,按照目录顺序逐步学习,并重点理解DRCC建模思想与Matlab/YALMIP/CPLEX等工具的集成使用方式,同时可参考文中列出的同类研究方向拓展研究思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值