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

news2024/11/25 1:00:03

文章目录

  • 前言
  • 一、函数指针的使用
    • 1. 加减乘除计算器普通实现
    • 2. 加减乘除计算机函数指针实现
  • 二、函数指针数组
    • 1. 函数指针数组的书写
    • 2. 两个有趣的代码
    • 3. 函数指针数组的使用
  • 三、指向函数指针数组的指针
  • 四、指针进阶_冒泡排序
    • 1.整型冒泡排序
    • 2. C语言qsort函数
    • 3. 仿写C语言qsort库函数
  • 总结


前言

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、指针进阶版的冒泡排序等介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1633965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第十二章 案例二:配置Trunk,实现相同VLAN的跨交换机通信

1、实验环境 公司的员工人数已达到 100 人&#xff0c;其网络设备如图12.13所示&#xff0c;现在的网络环境导致广播较多网速慢&#xff0c;并且也不安全&#xff0c;公司希望按照部门划分网络&#xff0c;并且能够保证一定的网络安全性 图12.13 实验案例二拓扑图 其网络规划…

【AIGC调研系列】来认识一下:WebLlama

WebLlama是一个基于Meta Llama 3构建的代理&#xff0c;专门为了网页导航和对话进行了微调。它是由McGill University的自然语言处理团队开发的研究项目&#xff0c;旨在通过对话进行网页浏览的智能代理[1][2]。WebLlama的目标是构建有效的人为中心的代理&#xff0c;帮助用户浏…

格雷希尔E10系列大电流测试连接器,在新能源汽车大电流接插件的电气测试方案

在新能源汽车的电驱动、电池包等设备的电测试处理中&#xff0c;格雷希尔E10系列电测试连接器具有显著的优势。E10系列的核心设计——插孔/插针&#xff0c;可以达到实验室10万次的插拔寿命&#xff0c;相比传统公母电接头500次左右的连接寿命&#xff0c;E10系列无疑大大减少测…

PCL 梯度滤波

文章目录 一、简介一、简介二、实现代码三、实现效果参考资料一、简介 一、简介 点云梯度滤波是指基于每个点与邻近点之间的倾斜程度进行滤波的算法,其原理也很简单,如下图所示: CloudCompare中的做法是: d i r = A B

网盘——删除常规文件

本文主要讲解网盘中文件操作部分的删除常规文件部分&#xff0c;具体实施步骤如下&#xff1a; 目录 1、具体步骤&#xff1a; 2、代码实现 2.1、添加删除常规文件的协议 2.2、添加删除常规文件槽函数 2.3、关联槽函数 2.4、添加槽函数定义 2.5、服务器回复 2.6、客户…

推荐一个wordpress免费模板下载

首页大背景图&#xff0c;首屏2张轮播图&#xff0c;轮换展示&#xff0c;效果非常的炫酷&#xff0c;非常的哇噻&#xff0c;使用这个主题搭建的wordpress网站&#xff0c;超过了200个&#xff0c;虽然是一个老主题了&#xff0c;不过是经得起时间考验的&#xff0c;现在用起来…

06|LangChain | 从入门到实战 -六大组件之Agent

点点赞~ 注意&#xff1a;langchain的版本迭代比较快&#xff0c;社区维护&#xff0c;代码当中或许部分方法在某个版本不再支持 01&#xff5c;LangChain | 从入门到实战-介绍 02&#xff5c;LangChain | 从入门到实战 -六大组件之Models IO 03&#xff5c;LangChain | 从入…

《R语言与农业数据统计分析及建模》——多重共线性和逐步回归

一、多重共线性 多重共线性&#xff1a;在多元线性回归时&#xff0c;多个自变量之间存在高度相关关系&#xff0c;时模型估计失真或难以估计准确的情况。 一般地&#xff0c;多元线性回归中自变量间应尽量相互独立。常规模型诊断方法难以检测多重共线性。 1、案例解释 作物产…

嵌入式学习65-C++(继承.派生和QT布局管理)

知识零碎&#xff1a; 信号合槽&#xff1a; 对象间通信 …

《动手学深度学习(Pytorch版)》Task03:线性神经网络——4.29打卡

《动手学深度学习&#xff08;Pytorch版&#xff09;》Task03&#xff1a;线性神经网络 线性回归基本元素线性模型损失函数随机梯度下降 正态分布与平方损失 线性回归的从零开始实现读取数据集初始化模型参数定义模型定义损失函数定义优化算法训练 线性回归的简洁实现读取数据集…

Find My无人机|苹果Find My技术与无人机结合,智能防丢,全球定位

无人机是利用无线电遥控设备和自备的程序控制装置操纵的不载人飞机&#xff0c;或者由车载计算机完全地或间歇地自主地操作。无人机按应用领域&#xff0c;可分为军用与民用。军用方面&#xff0c;无人机分为侦察机和靶机。民用方面&#xff0c;无人机行业应用&#xff0c;是无…

【介绍下Selenium】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

机器学习:深入解析SVM的核心概念(问题与解答篇)【二、对偶问题】

对偶问题 **问题一&#xff1a;什么叫做凸二次优化问题&#xff1f;而且为什么符合凸二次优化问题&#xff1f;**为什么约束条件也是凸的半空间&#xff08;Half-Space&#xff09;凸集&#xff08;Convex Set&#xff09;半空间是凸集的例子SVM 约束定义的半空间总结 **问题二…

Web 服务器解析漏洞 原理以及修复方法

漏洞名称 &#xff1a;Web服务器解析漏洞 漏洞描述&#xff1a; 服务器相关中间件存在一些解析漏洞&#xff0c;攻击者可通过上传一定格式的文件&#xff0c;被服务器的中间件进行了解析&#xff0c;这样就对系统造成一定危害。常见的服务器解析漏洞涉及的中间件有IIS&#x…

踏上R语言之旅:解锁数据世界的神秘密码(四)

文章目录 前言一、多元线性回归1.多元线性回归模型的建立2.多元线性回归模型的检验 二、多元线性相关分析1.矩阵相关分析2.复相关分析 三、回归变量的选择方法1.变量选择准则2.变量选择的常用准则3.逐步回归分析 总结 前言 回归分析研究的主要对象是客观事物变量间的统计关系。…

操作系统安全:安全审计,Windows系统日志详解,Windows事件ID汇总

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

使用OkHttp 缓存 API 调用提高Android应用性能

使用OkHttp 缓存 API 调用提高Android应用性能 坦率地说&#xff0c;我们都遇到过这样的情况——焦急地刷新应用&#xff0c;看着加载图标不停地旋转&#xff0c;等待那个至关重要的 API 响应。这样的等待我们已经是炉火纯青了&#xff0c;是吧&#xff1f;手指有节奏地轻敲屏…

【Node.js工程师养成计划】之express框架

一、Express 官网&#xff1a;http://www.expressjs.com.cn express 是一个基于内置核心 http 模块的&#xff0c;一个第三方的包&#xff0c;专注于 web 服务器的构建。 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用&…

LabVIEW机械臂控制与图像处理示教平台

LabVIEW机械臂控制与图像处理示教平台 随着工业自动化技术的快速发展&#xff0c;工业机器人在制造业中的应用越来越广泛&#xff0c;它们在提高生产效率、降低人工成本以及保证产品质量方面发挥着重要作用。然而&#xff0c;传统的工业机器人编程和操作需要专业知识&#xff…

React正式更新!开始学习React 19!

本文为原创文章&#xff0c;原文链接&#xff1a;J实验室&#xff0c;未经授权请勿转载 今年2月份&#xff0c;React 发布消息确认今年发布 v19 版本&#xff0c;尘封两年的版本号终于要更新了&#xff08;详情点击&#xff1a;React 19 发布在即&#xff0c;抢先学习一下新特性…