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

文章目录

  • 前言
  • 一、函数指针的使用
    • 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、指针进阶版的冒泡排序等介绍

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔试刷题-Day11

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 一、游游的水果大礼包 题目链接&#xff1a;https://ac.nowcoder.com/acm/problem/255193 类型&#xff1a;求一个表达式的最值&#xff08;并不是贪心,因为该题条件太少&…

PC 自动化测试入门 - pywinauto 上篇:初识

文章目录 前言PC 自动化测试 是什么&#xff1f;常用 PC 自动化测试工具pywinauto 是什么&#xff1f;Windows上支持的可访问性技术列表 操作记事本自动写入问题app Application(backend"uia").start("notepad.exe") 无法正常启动组件选择器和 print_cont…

clickhouse学习笔记05

ClickHouseSpringBoot2.XMybatisPlus整合搭建 添加需要的依赖&#xff1a; 添加clickhouse依赖&#xff1a; 配置数据库配置&#xff1a; 我们框架就搭建完了。 ClickHouse的项目案例统计需求讲解 ClickHouse的项目案例统计库表和数据准备 添加数据&#xff1a; 数据都插入进来…

算法必备数学基础:图论方法由浅入深实践与应用

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

python 入门第三天(高级进阶:str、set、dict、slice、推导式、高级变量类型的公共语法)

一、字符串str 字符串就是一串字符&#xff0c;是编程语言中表示文本的数据类型 1. 字符串定义 Python中可以使用一对双引号或者一对单引号定义字符串 str1 hello str2 "hello" 2. 获取字符串中元素 和列表一样&#xff0c;字符串也是通过索引获取元素 str …

CentOS7上安装部署Consul服务(小白版)

文章目录 1.Consul服务介绍2.Consul服务下载安装3.Consul服务配置3.1.创建Consul服务的运行用户3.2.下载服务配置生成脚本3.3.配置执行脚本需要的临时变量3.4.生成配置文件3.5.启动测试3.6.开机自启配置 1.Consul服务介绍 Consul 是一种开源的服务网格解决方案&#xff0c;由 …

pytorch库 01 安装Anaconda、Jupyter,Anaconda虚拟环境连接pycharm

文章目录 一、安装Anaconda1、卸载Anaconda&#xff08;可选&#xff09;2、下载并安装Anaconda3、配置环境变量4、桌面快捷方式 二、安装 PyTorch&#xff08;GPU 版&#xff09;库1、创建虚拟环境&#xff0c;并安装一些常用包2、GPU 基础3、检查驱动4、安装CUDA&#xff08;…

Linux搭建局域网私有yum仓库/配置本地光盘镜像仓库/搭建公有yum仓库--7700字详谈

帮助与补全功能 1.补全 yum &#xff08;options&#xff09;COMMAND check check-update clean deplist downgrade erase fs fssnapshot groups help history info install list makecache provides reinstall repo-pkgs repolist search shell swap update update-minimal …

每周一算法:单源次短路

题目描述 “您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。 比荷卢经济联盟有很多公交线路。每天公共汽车都会从一座城市开往另一座城市。沿途汽车可能会在一些城市&#xff08;零或更多&#xff09;停靠。 旅行社计划旅途从 S S S 城市出发&#xff0c;到 F …

新书速览|ChatGLM3大模型本地化部署、应用开发与微调

实战文本生成、智能问答、信息抽取、财务预警应用开发&#xff0c;掌握ChatGLM3大模型部署、开发与微调技术 01 本书内容 《ChatGLM3大模型本地化部署、应用开发与微调》作为《PyTorch 2.0深度学习从零开始学》的姊妹篇&#xff0c;专注于大模型的本地化部署、应用开发以及微…

挤压激励注意力 SE | Squeeze-and-Excitation Networks

论文名称&#xff1a;《Squeeze-and-Excitation Networks》 论文地址&#xff1a;https://arxiv.org/pdf/1709.01507.pdf 代码地址&#xff1a; https://github.com/hujie-frank/SENet 卷积神经网络 (CNN) 的核心构建块是卷积运算符&#xff0c;它使网络能够通过在每一层的局…

C++ | Leetcode C++题解之第50题Pow(x,n)

题目&#xff1a; 题解&#xff1a; class Solution { public:double quickMul(double x, long long N) {if (N 0) {return 1.0;}double y quickMul(x, N / 2);return N % 2 0 ? y * y : y * y * x;}double myPow(double x, int n) {long long N n;return N > 0 ? qu…

谷歌CEO谈拥有“最好的”AI、1000 种新云产品和Workspace

谷歌首席执行官桑达尔皮查伊 (Sundar Pichai) 在谷歌财报中发表了大胆言论&#xff0c;其中包括将 Workspace 吹捧为网络安全领域的领导者、谷歌云和 YouTube 到今年年底的总运行额将达到 1000 亿美元&#xff0c;以及为什么需要“强大的合作伙伴计划”来推动人工智能发展。 谷…

70、栈-最小栈

思路&#xff1a; 除了最后一个获取最小值以外&#xff0c;其他都可以使用一个栈来实现&#xff0c;但是如果当前一个最小值被移除了&#xff0c;如果获取第二小的值&#xff0c;这个是需要记录的。所以最好的办法是两个栈。一个作为主栈存放数据&#xff0c;一个作为辅栈&…

C++之类和对象

目录 一&#xff1a;再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit关键字 二. static成员 2.2 特性 三. 友元 3.1 友元函数 3.2 友元类 四&#xff1a; 内部类 五&#xff1a;匿名对象 六. 再次理解类和对象 一&#xff1a;再谈构造函数 1.1 构造…

关于discuz论坛网址优化的一些记录(网站地图sitemap提交)

最近网站刚上线&#xff0c;针对SEO做了些操作&#xff0c;为了方便网站网页百度被收录&#xff0c;特此记录下 discuz有免费的sitemap插件可以用&#xff0c;打开后台管理&#xff0c;找到插件栏&#xff0c;然后找到更多插件&#xff0c;进入插件市场。 选择这个免费的sitem…

ios CI/CD 持续集成 组件化专题四-(手动发布私有库-组件化搭建)

一 、创建私有索引库 1.1 、第一步 首先检查本地是否存在需要的私有索引库 pod repo list 例如&#xff1a;dp_base_ios_spec 在本地不存在该私有索引库 1.2 、第二步 在git下下创建一个新的库&#xff0c;这个库用来保存私有库的podspec文件&#xff0c;取名叫xxxSpec用以…

计算机组成实验(5)

一、实验目的和要求 1.1 实验目的 1. 复习二进制加减、乘除的基本法则 2. 掌握补码的基本原理和作用 3. 了解浮点数的表示方法及加法运算法则 4. 进一步了解计算机系统的复杂运算操作 1.2 实验要求 1. 熟悉二进制原码补码的概念,了解二进制加减乘除的原理与操作实现。 …

力扣HOT100 - 207. 课程表

解题思路&#xff1a; class Solution {public boolean canFinish(int numCourses, int[][] prerequisites) {int[] inDegree new int[numCourses];//存每个结点的入度List<List<Integer>> res new ArrayList<>();//存结点之间依赖关系Queue<Integer>…

buuctf——web题目练习

1.极客大挑战2019 easysql 密码或者用户输入万能密码即可 关于万能密码的理解和原理&#xff0c;可以参考这篇BUUCTF[极客大挑战 2019] EasySQL 1_[极客大挑战 2019]easysql 1-CSDN博客 2.极客大挑战2019 have fun 题目源码 需要构造payload 网页传参可参考&#xff1a;…