C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针,指针进阶版的冒泡排序等介绍


前言

C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍

一、函数指针的使用

1. 加减乘除计算器普通实现

#include <stdio.h>

void menu()
{
	printf("***************************\n");
	printf("*****  1. Add  2. Sub******\n");
	printf("*****  3. Mul  4. Div******\n");
	printf("*****  0. Quit       ******\n");
	printf("***************************\n");
}

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

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		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 :
			printf("输入错误\n");
			break;
		}
	} while (input);
	return 0;
}

2. 加减乘除计算机函数指针实现

  • 在上述基本实现中,每种情况(case)的代码大量冗余,可以使用函数指针来优化
  • 每种情况都调用 calc 函数,传入要进行运算的函数地址
  • 使用函数指针可以减少大量代码的冗余。
#include <stdio.h>

void menu()
{
	printf("***************************\n");
	printf("*****  1. Add  2. Sub******\n");
	printf("*****  3. Mul  4. Div******\n");
	printf("*****  0. Quit       ******\n");
	printf("***************************\n");
}

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 calc(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;

	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:
			printf("输入错误\n");
			break;
		}
	} while (input);
	return 0;
}

二、函数指针数组

  • 存放函数指针的数组就是函数指针数组。

1. 函数指针数组的书写

// 函数指针的书写
int Add(int x, int y);
int (*pf)(int, int)  = &Add;
// 以上为函数指针的书写,函数指针数组的书写与函数指针可以通过函数指针的修改来得到
// 函数指针数组,顾名思义,一个数组的每个元素的类型都是函数指针。
int (*pfarr[5])(int, int) = { Add, Sub, Mul, Div};
// Add Sub Mul Div 均为函数名,即函数的地址
// pfarr[5] 是一个数组名为pfarr,数组元素个数为5 的数组
// 这个数组的元素类型是 int (*)(int, int),即函数指针类型。

2. 两个有趣的代码

( *(void(*)())0)();
  • 上述代码,void(*)() 是一个函数指针类型,带括号跟0,是将0强制转化为函数指针类型。
  • 最后再调用转换后的 0 地址对应的函数,没有参数。
void(*signal(int, void(*)(int)))(int);
  • 上述代码,本质上是一个函数的声明,函数名是 signal
  • signal函数的参数有两个,一个是 int 即 整型类型
  • 一个是 void(*)(int) 即 函数指针类型的,并且这个函数指针指向的函数参数有一个 int 即整型类型,并且返回值的类型是 void。
  • signal函数的返回值是 void(*)(int)即函数指针类型,也就是说它返回了一个函数的地址

上述 2 中的代码可以简化为更好理解的形式借助 typedef

typedef void (*pf_t)(int); // 这个语句将 void (*)(int) 类型重命名为 pf_t
// 所以 2 中的代码可以写成
pf_t signal(int ,pf_t);
// 这样就非常清晰了,signal 函数的返回值是 pf_t, 参数类型 为 int 和 pf_t

3. 函数指针数组的使用

  • 以 一、函数指针的使用中的计算器为例。
  • 使用函数指针数组,省略了switch分支语句的使用,更加节省了代码量
  • 最为关键的是,如果将来计算机需要增加功能,可以直接再函数指针数组中加入功能函数的地址即可。便于新增或减少计算器功能。
#include <stdio.h>

void menu()
{
	printf("***************************\n");
	printf("*****  1. Add  2. Sub******\n");
	printf("*****  3. Mul  4. Div******\n");
	printf("*****  0. Quit       ******\n");
	printf("***************************\n");
}

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

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		----------------------------------------------------------------
		int (*pfarr[5])(int, int) = {0, Add, Sub, Mul, Div};
		int sz = sizeof(pfarr) / sizeof(pfarr[0]);
		if (0 == input)
		{
			printf("退出游戏\n");
			break;
		}
		else if (input >= 1 && input < sz)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			int ret = pfarr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误\n");
		}
		----------------------------------------------------------------
	} while (input);
	return 0;
}

三、指向函数指针数组的指针

简单介绍

  • 通过函数指针数组的书写 -----> 指向函数指针数组的指针
int (*pfarr[5])(int, int) = {Add, Sub, Mul, Div};

// 指向函数指针数组的指针,则需要取地址 函数指针数组
&pfarr;
// 用 *ppfarr来接收 指向函数指针数组的指针
// (*ppfarr)[5] 说明 指向函数指针数组的指针 所指向的数组有 5 个元素
// int (* (*ppfarr)[5])(int, int) 说明 指向函数指针数组的指针 所指向的数组元素的类型为 int(*)(int, int) 
// 即函数指针
int (* (*ppfarr)[5])(int, int) = &pfarr;

四、指针进阶_冒泡排序

1.整型冒泡排序

#include <stdio.h>

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	int j = 0;
	int tmp = 0;
	for (i = 0; i < sz - 1; i++)
	{

		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

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

	bubble_sort(arr, sz);

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

2. C语言qsort函数

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
  • C语言中qsort函数的定义如上所示,
  • base 是 数组的起始地址,为空指针,可以接收任何类型的地址。
  • num 是 数组元素的个数。
  • width 是 一个数组元素所占字节大小。
  • compare指向一个比较函数,比较函数有两个参数,分别为要比较的两个数。
    注意:空指针是指没有具体类型指针,可以接受任何类型的指针,但是不能直接被解引用的

3. 仿写C语言qsort库函数

  • C语言的qsort函数可以比较任意类型的数据大小,并按照快速排序法进行排序。
  • 我们这里采用 冒泡排序仿写 qsort。
  • 函数名 bubble_sort
  • 函数参数 :
    1. 空指针 base 为了接收任何类型的数据
    1. sz 数组元素的大小
    1. width 一个数组元素所占字节大小
    1. *cmp 指向一个比较函数, 如:整形比较 cmp_int ,这个函数如果传入参数第一个大于第二个则返回大于零的数,如果相等,则返回0,如果小于,返回-1,可以直接用第一个数减去第二个数得到。
    1. swap 交换函数,传入两个值的地址,一个字节一个字节的进行交换。
  1. 整型冒泡排序
#include <stdio.h>
--------------------------------------------------------
// 比较整型的具体函数,它的调用者在bubble_sort中
int cmp_int(void* e1, void* e2)
{
	/*if (*(int*)e1 > *(int*)e2)
	{
		return 1;
	}
	else if (*(int*)e1 == *(int*)e2)
	{
		return 0;
	}
	else
	{
		return -1;
	}*/
	return (*(int*)e1 - *(int*)e2);
}
--------------------------------------------------------
// 交换函数 交换两个数的值,一个字节一个字节交换 它的调用者在bubble_sort中
void Swap(char* buff1, char* buff2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buff1;
		*buff1 = *buff2;
		*buff2 = tmp;
		buff1++;
		buff2++;
	}
}
--------------------------------------------------------
// 进行冒泡排序的逻辑
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{
	int i = 0;
	int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz-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);
				// 调用交换函数
				flag = 0;
			}
			
		}
		if (1 == flag)
		{
			break;
		}
		
	}
}
--------------------------------------------------------
// 调用整型冒泡排序
void test1()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
--------------------------------------------------------
//主函数
int main()
{
	test1(); // 整型排序
	//test2(); // 结构体中name字符的排序

	return 0;
}

执行结果如下:
在这里插入图片描述

  1. 结构体内字符串排序

#include <stdio.h>
-----------------------------------------------------------------
// 声明结构体类型
struct Stu
{
	char name[20];
	int age;
};
-----------------------------------------------------------------
// 结构体内字符串比较函数
int cmp_struct_by_name (void* e1, void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
	// strcmp 函数返回的值 正好是 1 0 -1
}
-----------------------------------------------------------------
// 交换函数
void Swap(char* buff1, char* buff2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buff1;
		*buff1 = *buff2;
		*buff2 = tmp;
		buff1++;
		buff2++;
	}
}
-----------------------------------------------------------------
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{
	int i = 0;
	int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz-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);
				flag = 0;
			}
			
		}
		if (1 == flag)
		{
			break;
		}
		
	}
}
-----------------------------------------------------------------
// 调用比较结构体内部的字符的排序
void test2()
{
	struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };
	int sz = sizeof(S) / sizeof(S[0]);

	bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_name);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", S[i].name);
	}
}
-----------------------------------------------------------------
int main()
{
	//test1(); // 整型排序
	test2(); // 结构体中name字符的排序

	return 0;
}

执行结果如下:
在这里插入图片描述

  1. 结构体内整型排序
#include <stdio.h>
--------------------------------------------------------------------
// 结构体类型声明
struct Stu
{
	char name[20];
	int age;
};
int cmp_struct_by_age(void* e1, void* e2)
{
	return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
--------------------------------------------------------------------
// 交换函数定义
void Swap(char* buff1, char* buff2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buff1;
		*buff1 = *buff2;
		*buff2 = tmp;
		buff1++;
		buff2++;
	}
}
--------------------------------------------------------------------
// 冒泡函数定义
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{
	int i = 0;
	int flag = 1;// 如果为有序数组,进行一趟排序,如果没有交换,则直接跳出
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz-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);
				flag = 0;
			}
			
		}
		if (1 == flag)
		{
			break;
		}
		
	}
}
--------------------------------------------------------------------
//调用排序结构体类型中的整型
void test3()
{
	struct Stu S[3] = { {"zhangsan", 15}, {"lisi", 50},{"wangwu", 35} };
	int sz = sizeof(S) / sizeof(S[0]);

	bubble_sort(S, sz, sizeof(S[0]), cmp_struct_by_age);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", S[i].age);
	}
}
--------------------------------------------------------------------
int main()
{
	//test1(); // 整型排序
	//test2(); // 结构体中name字符的排序
	test3(); // 结构体中的age排序

	return 0;
}

执行结果如下:
在这里插入图片描述


总结

C语言函数指针的使用、函数指针数组及使用、指向函数指针数组的指针及使用,C语言库函数qsort、指针进阶版的冒泡排序等介绍

  • 44
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值