C语言—深度剖析函数指针,函数指针数组

我们先来看一段代码

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

输出的是两个地址,这两个地址是 test 函数的地址。

那我们的函数的地址要想保存起来,怎么保存?

下面我们看代码:

void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?

答案是:

pfun1可以存放。

pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参 数,返回值类型为void。

阅读两段有趣的代码:

//代码1
(*(void (*)())0)();
//(void (*)())函数指针类型
//把int类型0强制转化成函数指针(void (*)()),相当于把0强制转化成一个函数的地址
//*解引用函数的地址,找到函数,最后加()传空值
//妙
//理解为调用0作为地址处的函数
//代码2
void (*signal(int , void(*)(int)))(int);
//signal一定是函数名
//(int , void(*)(int)),有两个参数分别是int和函数指针
//void (*)(int);剩下的部分说明声明了一个函数指针
//所以以上代码是一次函数声明,声明的是signal函数的第一个函数是int,第二个参数的返回类型是函数指针,该函数指向的函数参数是int,返回类型是void,signal函数的返回类型是一个函数指针,该函数的参数是int,返回类型是void

推荐《C陷阱和缺陷》 这本书中提及这两个代码。

代码2太复杂,如何简化:

typedef void(*pfun_t)(int);//把void(*)(int)类型重命名为pfun_t
pfun_t signal(int, pfun_t);

计算器小例子

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a*b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf( "*************************\n" );printf( " 1:add           2:sub \n" );printf( " 3:mul           4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);switch (input){case 1:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = add(x, y);printf( "ret = %d\n", ret);break;case 2:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = sub(x, y);printf( "ret = %d\n", ret);break;case 3:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = mul(x, y);printf( "ret = %d\n", ret);break;case 4:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = div(x, y);printf( "ret = %d\n", ret);break;case 0:printf("退出程序\n");breark;default:printf( "选择错误\n" );break;}} while (input);return 0;
}       

是不是冗余的部分很多,我们应该如何去优化一下呢?

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
void calc(int (*pf)(int, int))
{int x, y;int ret = 0;printf("输入操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}
int main()
{int input = 1;do{printf("*************************\n");printf(" 1:add           2:sub \n");printf(" 3:mul           4:div \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);switch (input){case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);case 4:calc(div);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,

把函数指针放在数组中就是函数指针数组

比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

所以我们上面的计算器是不是还可以再次优化一下

首先我们来看函数指针

int (*pf)(int,int) = Add;

此时pf是函数指针,改为函数指针数组也十分简单,我们只需要在指针名后加上方括号即可

int (*arr[4])(int,int) = {Add,Sub,Mul,Div};

函数指针数组的用途:转移表

来重新看一下上面的计算器

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a*b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf( "*************************\n" );printf( " 1:add           2:sub \n" );printf( " 3:mul           4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);switch (input){case 1:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = add(x, y);printf( "ret = %d\n", ret);break;case 2:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = sub(x, y);printf( "ret = %d\n", ret);break;case 3:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = mul(x, y);printf( "ret = %d\n", ret);break;case 4:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = div(x, y);printf( "ret = %d\n", ret);break;case 0:printf("退出程序\n");breark;default:printf( "选择错误\n" );break;}} while (input);return 0;
}

如果我们用函数指针数组实现

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a*b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表while (input){printf( "*************************\n" );printf( " 1:add           2:sub \n" );printf( " 3:mul           4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);if ((input <= 4 && input >= 1)){printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = (*p[input])(x, y);}elseprintf( "输入有误\n" );printf( "ret = %d\n", ret);}return 0;
}

是不是就是很简单了

指向函数指针数组的指针

void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函数指针pfunvoid (*pfun)(const char*) = test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;
}

指向函数指针数组的指针是一个 指针

指针指向一个 数组 ,数组的元素都是 函数指针 ;

回调函数

所以,我们来看一下C语言提供的排序算法的使用

qsort函数的使用:

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{return (*( int *)p1 - *(int *) p2);
}
int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++){printf( "%d ", arr[i]);}printf("\n");return 0;
}

使用回调函数,模拟实现qsort(采用冒泡的方式)。

#include <stdio.h>
int int_cmp(const void * p1, const void * p2)
{return (*( int *)p1 - *(int *) p2);
}
void _swap(void *p1, void * p2, int size)
{int i = 0;for (i = 0; i< size; i++){char tmp = *((char *)p1 + i);*(( char *)p1 + i) = *((char *) p2 + i);*(( char *)p2 + i) = tmp;}
}
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{int i = 0;int j = 0;for (i = 0; i< count - 1; i++){for (j = 0; j<count-i-1; j++){if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0){_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);}}}
}
int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };//char *arr[] = {"aaaa","dddd","cccc","bbbb"};int i = 0;bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++){printf( "%d ", arr[i]);}printf("\n");return 0;
}

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

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

相关文章

java常用时间日期函数

日期格式化方式 SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat sdf new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); //yyyy-MM-dd对于年份取值取得是当天所在的这个年份//YYYY-MM-dd它所取的年份是本周所在的这个…

Electron 30.0.0 发布,升级 Node 和 V8 引擎

近日&#xff0c;Electron 30.0.0 正式发布&#xff01;你可以通过 npm install electronlatest 进行安装&#xff0c;或者从 Electron 的发布网站下载&#xff0c;继续阅读了解此版本的详细信息。 &#x1f525; 主要更新 Windows 上支持 ASAR 完整性融合。如果未正确配置&am…

软件测试——Postman Script脚本功能

Postman作为软件测试里一款非常流行的调试工具&#xff0c;给我们提供了一个执行JavaScript脚本的环境&#xff0c;所以我们可以使用js语言编写脚本来解决一些接口自动化的问题&#xff0c;比如接口依赖、接口断言等等。Postman有Pre-RequestScript和Tests两个编写js脚本的模块…

[学习] linux命令大全

linux命令 前言1. 文件管理*mv* : 移动或改名文件*ls*: 显示目录中文件及其属性*cp*: 复制文件或目录*mkdir*: 创建目录文件*pwd*: 显示当前工作目录的路径*tar*: 压缩和解压缩文件*cd*: 切换目录*chmod*: 改变文件或目录权限 2&#xff1a;文档编辑*cat*: 在终端设备上显示文件…

Jenkins 哲学 - 插件初始化安装失败

到Jenkins官网查找最新的LST版本 最后的版本号一定要带&#xff0c;指定下载具体的版本号 docker pull jenkins/jenkins:2.426.1 自定义挂载目录&#xff0c;修改权限 mkdir /jenkins/jenkins_homechmod 777 /data/jenkins

Ansible安装基本原理及操作(初识)

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月23日15点18分 Ansible 是一款功能强大且易于使用的IT自动化工具&#xff0c;可用于配置管理、应用程序部署和云端管理。它使用无代理模式&#xff08;agentles…

谈谈mysql中的各个关键字

1.为什么学习mysql mysql是当今最主流且开放源码的关系型数据库&#xff0c;开发者为瑞典 MySQL AB 公司。目前 MySQL 被广泛地应用在 Internet 上的中小型网站中。由于其体积小、速度快、总体拥有成本低&#xff0c;尤其是开放源码这一特点&#xff0c;许多中小型网站为了降低…

GetManifestResourceStream用法

项目添加资源文件Resource1.resx “C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools\ResGen.exe” Resource1.resx 从“Resource1.resx”读入 1 资源 正在写入资源文件… 完成。 排除资源文件Resource1.resx 添加文件Resource1.resources属性生成…

由于使用校园网,ubuntu虚拟机时而不能连接网络的解决方案

本来配置好了网络的虚拟机第二天又没网了,ping不通 在尝试过诸如下面的解决方案仍没有丝毫好转(这个时候意识到可能是用了校园网的缘故) VMWARE Ubuntu虚拟机和Windows10主机ping不通|Ubuntu断网、没有网络_vmware 桥接模式下 主机无法ping ubuntu虚拟机来自 192.168.110.95 的…

【C语言】每日一题,快速提升(10)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;圣诞树 输入&#xff1a; 1输出&#xff1a; * * * * * **说明&#xff1a; 输入&#xff1a; 2输出&#xff1a; * * * * * * * …

C++:基础语法

一、命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或名字污染&#xff0c;n…

lacp的详解

LACP&#xff08;Link Aggregation Control Protocol&#xff09;是一种用于在网络中组合多个物理链路以增加带宽和冗余的协议。它是IEEE 802.3ad标准中定义的一种链路聚合技术。 LACP允许将多个物理链路绑定成一个逻辑链路&#xff0c;称为聚合链路或聚合组。这个聚合组可以提…

【C++】一篇文章带你深入了解list

目录 一、list的介绍二、 标准库中的list类2.1 list的常见接口说明2.1.1 list对象的常见构造2.1.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/list/list/list/)2.1.1.2 [有参构造函数(构造并初始化n个val)](https://legacy.cplusplus.com/reference/list/list/…

上位机图像处理和嵌入式模块部署(树莓派4b开机启动脚本)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 编写好程序之后&#xff0c;一般要求程序开机启动后就可以运行。所以这个时候&#xff0c;我们一般就会把程序流程放在开发板的启动脚本当中。如果…

Android 8.1 删除Launcher桌面搜索框

Android 8.1 删除Launcher桌面搜索框 最近接到项目反馈&#xff0c;要求删除Launcher桌面的搜索框&#xff0c;具体修改参照如下&#xff1a; /vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/config/BaseFlags.java public static final b…

开源大模型应该怎么选?

文章目录 前言为什么选择开源模型而不是商业模型?开源模型对比Llama 2Mixtral 8x7BZephyr 7BSOLAR 10.7BCode Llama 专用 Vs. 通用生产环境部署LLMs 的注意事项 前言 在过去的一年里&#xff0c;人工智能领域不断涌现出各种大语言模型(LLMs)&#xff0c;每个模型都在不断突破生…

代码随想录算法训练营day1 | 704. 二分查找、27. 移除元素

数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 因为数组的在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;需要移动其他元素的地址。 那么二维数组在内存的空间地址是连续的么&#xff1f;不同编程语言的内存管理是不…

SpringBoot之@Conditional衍生条件装配详解

文章目录 ☃️前言☃️简介☃️示例❄️❄️ConditionalOnProperty❄️❄️ConditionalOnClass❄️❄️ConditionalOnBean❄️❄️自定义条件 ☃️SpringBoot源码中使用☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客…

pET-28a(+)是什么,怎么看?-实验操作系列-1

01 典型的pET-28a()质粒遗传图谱 02 元件解读 Origin复制子&#xff1a;ColE1/pMB1/pBR322/pUC ori——起始载体的复制&#xff1b;f1 ori——f1噬菌体复制子&#xff0c;显示正义链合成方向。The origin of replication&#xff0c;由复制起始位点和相关调控元件组成&#xf…

Midjourney-01 初试上手 注册使用并生成你的第一张AI图片 详细流程 提示词 过程截图 生成结果 付费文生图的天花板!

背景介绍 Midjourney是一款基于人工智能技术的绘画软件&#xff0c;利用深度学习算法来辅助用户进行绘画创作。这款软件能够通过用户输入的文本描述生成图像&#xff0c;支持多种生成方式&#xff0c;包括文字生成图片、图片生成图片和混合图片生成图片。 图像生成方式&#…