函数指针数组的简单应用(计算器)以及qsort函数

目录

1.函数指针数组

2.函数指针数组的简单应用

2.qsort函数

3.qsort的模拟实现


1.函数指针数组

函数指针数组是存放函数指针的数组,其本质上还是数组,其中的元素为函数指针。

int test(int a,int b)
{
   return a+b;
}

int(*p)(int,int)=test;

我们可知p为函数test的指针,那函数指针数组应该怎么写呢?

int(*p1[1])(int,int)={p};

这需要在函数指针的基础上进行书写,只要在指针名的后面加上数组的大小即可。

分析:p1先与[]结合,说明p是数组,将数组取出,剩下:int (*)(int,int),这表示数组中的元素都为函数指针,函数返回值为int类型。

2.函数指针数组的简单应用

我们接下来利用函数指针数组实现一个整形简单计算器。

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;

}

我们首先想到的就是用switch语句进行不同分支的判断,然后再使用相应功能的函数进行计算。

int main()
{
	int num = 0;
	do
	{
		menu();
		scanf_s("%d", &num);
		switch (num)
		{
		case 0:
			printf("exit!\n");
			break;
		case 1:
			printf("请输入操作数:");
	        scanf_s("%d %d", &a, &b);
	        int ret = add(a, b);
	        printf("%d\n", ret);
			break;
		case 2:
			printf("请输入操作数:");
	        scanf_s("%d %d", &a, &b);
	        int ret = sub(a, b);
	        printf("%d\n", ret);
			break;
		case 3:
			printf("请输入操作数:");
	        scanf_s("%d %d", &a, &b);
	        int ret = mul(a, b);
	        printf("%d\n", ret);
			break;
		case 4:
			printf("请输入操作数:");
	        scanf_s("%d %d", &a, &b);
	        int ret = div(a, b);
	        printf("%d\n", ret);
			break;
		default:
			printf("请重新输入:");
		}
	} while (num);
	return 0;
}

正如这段代码所示,虽然可以成功实现功能,但代码冗余度太大,我们仔细观察,会发现每次进入case后,只有调用的函数不同,其余都相同,因此我们可以利用函数指针数组来简化代码。

int main()
{
	int num = 0;
	int a = 0;
	int b = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf_s("%d", &num);
		int(*ptarr[5])(int, int) = { 0,add,sub,mul,div };
		if (num > 0 && num < 5)
		{
			printf("请输入操作数:");
			scanf_s("%d %d", &a, &b);
			ret = ptarr[num](a, b);
			printf("%d\n", ret);
		}
		else if (num == 0)
			printf("exit!\n");
		else
			printf("请重新输入:\n");
	} while (num);
	return 0;
}

我们使用一个函数指针数组存放这四种运算,通过输入下标来调用相对应的函数来实现对应的功能。这样做就减少了冗余度,提高了代码的通用性。

除这两种方法之外,我们还可以利用回调函数来实现。

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

//回调函数;通过调用函数指针来实现被调用函数的功能
void crl(int(*p)(int, int))//形参为函数指针,p为函数指针,指向被调用函数
{
	int a = 0;
	int b = 0;
	printf("请输入操作数:");
	scanf_s("%d %d", &a, &b);
	int ret = p(a, b);//利用函数指针
	printf("%d\n", ret);
}


int main()
{
	int num = 0;
	do
	{
		menu();
		scanf_s("%d", &num);
		switch (num)
		{
		case 0:
			printf("exit!\n");
			break;
		case 1:
			crl(add);
			break;
		case 2:
			crl(sub);
			break;
		case 3:
			crl(mul);
			break;
		case 4:
			crl(div);
			break;
		default:
			printf("请重新输入:");
		}
	} while (num);
	return 0;
}

我们把重复的部分写成一个函数crl,形参为函数指针,所传入的实参为加减乘除的四个函数的地址。当我们想使用不同功能时,直接调用crl,只需要改变传入的实参部分即可。

2.qsort函数

void qsort (void* base, //指向了需要排序的数组的第一个元素
           size_t num, //排序的元素个数
           size_t size,//一个元素的大小,单位是字节
           int (*cmp)(const void*, const void*)//函数指针类型 - 这个函数指针指向的函数,能够比较base指向数组中的两个元素
           );

我们可以从cplusplus中得到qsort函数的定义,他的形参部分由以下四部分组成:指向需要排序数组首元素的无类型指针,需要排序的元素个数,一个元素的大小,指向一个可以实现比较功能的函数的指针。

以下为示例:

//qsort整形排序
int cmplete(const void* p1, const void* p2)
{
	return(*(int*)p1 - *(int*)p2);
}

void print(int arr[],int sz)

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


void test1()
{
	int arr[] = { 0,7,9,8,6,5,4,3,5,2,4,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmplete);
	print(arr, sz);
}

3.qsort的模拟实现

我们接下来用冒泡排序的思想来模拟实现qsort函数。

冒泡排序法:

void bubble_sort(int arr[], int sz)
	{
		int i = 0;
		//趟数
		for (i = 0; i < sz - 1; i++)
		{
			//一趟比较
			//两两相邻元素比较
			int j = 0;
			for (j = 0; j < sz - 1 - i; j++)
			{
				if (arr[j] > arr[j + 1])
				{
					int tmp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = tmp;
	     		}
			}
		}
	}

我们从中可以看到冒泡排序是有局限性的,它只能够排序整形,其他类型的就不能适用了。

用冒泡排序的思想模拟qsort有以下几点难处:

1.不确定数组类型

2.如何比较相邻两个元素的大小

3.如何交换相邻元素

以下是解决思路:

问题一:不确定数组类型

方法:我们将形参中的首地址元素改为void*类型,之后在使用时强转回所需类型(对应qsort函数形参中第一项)

问题二:如何比较相邻两个元素的大小

因为每种类型的比较方法大不相同,所以需要我们自己设计比较函数,(对应qsort函数形参中第四项)

问题三:如何交换相邻元素

因为每种类型的大小各不相同,因此,我们需要知道数组中的元素大小,然后一个字节一个字节的交换,最终实现交换相邻元素。(对应qsort函数形参中第三项)

以下代码为模拟qsort函数整形比较:

//测试整形比较
int cmp(void* p1, void* p2)//比较函数
{
	return(*(int*)p1 - *(int*)p2);
}

void swap(char* p1, char* p2,int size)//交换arr[j],arr[j+1],一个字节一个字节交换
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}
void my_qsort(void* base, int sz, int size, int (*cmp)(void* , void* ))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			//假设升序cmp返回>0,交换
			if ((cmp((char*)base + size * j, (char*)base + size * (j + 1))) > 0)//比较大小,需要比较数的地址,用char为基础得到地址
			{
				swap((char*)base + size * j, (char*)base + size * (j + 1),size);
			}
		}
	}
}

void test()
{
	int arr[] = { 0,7,9,8,6,5,4,3,5,2,4,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(arr[0]), cmp);
	print(arr,sz);
}

感谢您的观看!

本人拙见,大佬勿怪  ^v^

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值