深入理解指针(4)

目录

1.函数指针数组

 2.函数指针数组举例——转移表

3.回调函数

 4.qsort函数使用举例

4.1 整型类型

4.2 结构体类型

4.2.1 年龄排序

4.2.2 姓名比较 

5.模拟实现qsort函数


1.函数指针数组

在学习函数指针数组之前,我们先类比一下:

整型数组:存放整型的数组;

字符数组:存放字符的数组:

指针数值:存放地址的数组......

那函数指针数组:存放函数指针的数组?答案是:对的

函数指针数组是指针数组中的一种,这种数组中存放的函数指针(函数的地址),并且存放在函数指针数组中的元素类型要相同。

我们假设存在四种函数,函数的类型相同,分别是Add,Sub,Mul,Div.现在如果想将这四种函数放在一个数组中,我们该怎么做?接下来我们看看具体做法:

由于函数指针数组中存放的是函数的地址,所以我们需要创建一个指针变量:

int (* parr[ 4])(int,int )={ Add,Sub,Mul,Div };  parr为函数指针数组的名字,int (*)(int,int)为数组元素类型,parr[4]中的4表示数组中由4个元素。使用函数指针数组,我们可以通过数组下标找到函数。接下来我们画图来理解一下:

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

}
int main()
{
	int input = 0;
	int ret = 0;
	do {
		menu();
		int x = 0, y = 0;
		printf("请输入>>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个数>>");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个数>>");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个数>>");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个数>>");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}

上述代码中的每个case语句中都会出现几句相同的语句,我们是否可以将这几个重复出现的代码进行简化,当然是可以了。

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("***1.add 2.sub   ******\n");
	printf("***3.mul 4.div   ******\n");
	printf("*******  0.exit  ******\n");

}
int main()
{
	int ret = 0;
	int x=0, y = 0;
	int input = 0;
	do {

		menu();
		printf("请输入>>");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if (input > 0 && input <= 4)
		{
			int (*p[5])(int, int) = { NULL,add,sub,mul,div };//转移表
			printf("请输入两个整数>>");
			scanf("%d %d", &x, &y);
			ret = p[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

3.回调函数

不知道是否有小伙伴会有疑问,回调函数是什么?好像没有听过这个词语。OK,那让我来介绍一下回调函数。

回调函数就是一个通过函数指针调用的函数。(这句话应该会有很多小伙伴依然不理解)简单来说就是把一个函数的指针(函数指针中存放的是函数的地址,也就是函数的地址)作为参数传递给另一个函数 ,前面那个函数就被成为回调函数。

接下来,我们通过代码来看一下:

int add(int x, int y)
{
	return x + y;
}
void test(int (*pa)(int, int))
{
	int x = 0, y = 0;
	scanf("%d %d", &x, &y);
	int ret = pa(x, y);
	printf("%d\n", ret);
}
int main()
{
	test(add);
	return 0;
}

在这个函数中,我们将add这个加法函数的地址作为参数传递给test函数,然后我们在test函数中通过指针调用add函数(在上面代码中,add就是回调函数)。

注意:回调函数是被动调用,也就是在其他函数中被调用,也由这个函数传参

既然已经学习了回调函数,那我们是不是可以通过回调函数对刚才的代码进行改进:

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("***1.add 2.sub   ******\n");
	printf("***3.mul 4.div   ******\n");
	printf("*******  0.exit  ******\n");

}
void calc(int(*p)(int, int))
{
	int x = 0, y = 0;
	printf("请输入两个整数>>");
	scanf("%d %d", &x, &y);
	int ret = p(x, y);
	printf("%d\n", ret);
}
int main()
{
	int input = 0;
	do
	{
		menu();
		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:
			break;
		}
	} while (input);
	return 0;
}

 4.qsort函数使用举例

 qsort函数可以排序任意类型的顺序(默认是升序)

我们来解读一下qsort函数中的元素分别表示什么:

 通过上面的解读,基本上了解了qsort函数,接下来让我们使用qsort函数来排序整型类型和结构体类型:

4.1 整型类型
#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
void print(int* pa, size_t sz)
{
	int i = 0;
	while (i < sz)
	{
		printf("%d ", *(pa + i));
		i++;
	}
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);
	return 0;
}
4.2 结构体类型
4.2.1 年龄排序
#include<stdlib.h>
struct stu
{
	char name[20];
	int age;
};
int cmp_stu_age(const void* p1, const void* p2)
{
	return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}
void print(struct stu* ps, size_t sz)
{
	int i = 0;
	while (i < sz)
	{
		printf("%s:%d\n", ps->name, ps->age);
		ps++;
		i++;
	}
}
int main()
{
	struct stu s[] = {{"liaohanpeng",21},{"niwen",18},{"linshun",20}};
	size_t sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_age);
	print(s, sz);
	return 0;
}
4.2.2 姓名比较 
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[20];
	int age;
};
int cmp_stu_name(const void* p1, const void* p2)
{
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
void print(struct stu* ps, size_t sz)
{
	int i = 0;
	while (i < sz)
	{
		printf("%s:%d\n", ps->name, ps->age);
		ps++;
		i++;
	}
}
int main()
{
	struct stu s[] = { {"liaohanpeng",21},{"niwen",18},{"linshun",20} };
	size_t sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_name);
	print(s, sz);
	return 0;
}

上述代码中的strcmp函数会在接下来的博客中会详细介绍。

5.模拟实现qsort函数

int cmp_int(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 swap(char* bulf1, char* bulf2, int size)
{
	char tem = 0;
	for (int i = 0; i < size; i++)
	{
		tem = *bulf1;
		*bulf1 = *bulf2;
		*bulf2 = tem;
		bulf1++;
		bulf2++;
	}
}
void bubble_sort2(void * base, size_t sz, size_t size, int (*cmp)(const void* p1,const void* p2))
{
	int i = 0, j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base+j*size, (char*)base + (j+1) * size) > 0)//比较大小
			{
				//交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}
}
void test()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort2(arr,sz,sizeof(arr[0]),cmp_int);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

 (char*)base+j*size是第j个元素的地址, (char*)base + (j+1) * size)是第j+1个元素的地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值