带你深入了解c语言指针后续
前言
🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏: 🍔🍟🌯 c语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:介绍c语言中有关指针更深层的知识.
金句分享:
✨在该奋斗的岁月里,✨
✨对得起每一寸光阴.✨
目录
- 前言
- 一、函数指针
- 1.1 函数指针的创建
- 1.2 函数指针应用
- 二、函数指针数组
- 三、函数指针数组指针
- 四、回调函数
一、函数指针
1.1 函数指针的创建
函数指针,顾名思义,就一个指向函数的指针.
那么
整形指针是接收整形的地址;
字符指针是接收字符的地址.
数组指针是接收数组的地址
函数有地址吗?函数名又可以代表什么?
示例:自定义一个加法函数,观察函数的地址
//函数指针
#include <stdio.h>
int add(int a,int b)//自定义一个加法函数
{return a + b;
}
int main()
{int a = 2, b = 3;int c=add(a, b);printf("%d\n", c);//确认一下函数是否正确printf("%p\n", &add);//取出函数的地址printf("%p\n", add);//用数组名打印return 0;
}
运行结果:(在不同环境下地址会不一样,这里采用的是vs x86环境)
5
00E61023
00E61023
不难发现,函数名也可以代表函数的地址.
那么函数指针该怎么写呢?
以数组指针为例:
数组指针示例:写一个指向 int arr[10] 数组的数组指针;
第一步:
(*p) //先确定是一个指针
第二步:
(*p)[10] //确定指向的是一个有10个元素的数组
第三步:
int(*p)[10] //确定该数组元素为int型
第四步:
int(*p)[10]=&arr;//将数组的地址赋值给数组指针
//或者int(*p)[10]=arr;
示例2:
函数指针:指向int add(int a,int b)
第一步:
(*p) //先确定是一个指针
第二步:
(*p)(int,int) //确定指向的函数有两个参数
第三步:
int (*p)(int,int) //确定该函数的返回类型
第四步:
int (*p)(int,int)=&add;//将函数的地址赋值给函数指针
//等价于:int (*p)(int,int)=add;
1.2 函数指针应用
那么函数指针有什么用吗?
当然是用来调用函数了.
同样以数组指针为例:
🌰使用数组指针访问数组.
int main()
{int arr[10] = { 1,2,3,4,5 };int(*p)[10] = arr;for (int i = 0; i < 5; i++){printf("%d ", ( * p)[i]);}printf("\n");return 0;
}
🌰使用函数指针调用函数
#include <stdio.h>
int add(int a,int b)
{return a + b;
}
int main()
{int a = 2, b = 3;int(*p1)(int, int) = &add;int(*p2)(int, int) = add;int ret1=add(a,b);int ret2=(*p1)(a, b);//写法一int ret3=p1(a, b);//写法2printf("ret1=%d\nret2=%d\nret3=%d",ret1,ret2,ret3);return 0;
}
运行结果:都能完成函数的调用
ret1=5
ret2=5
ret3=5
二、函数指针数组
函数指针数组实质用于存放函数指针的数组.
即数组的成员都是函数指针.
写法:以数组成员都是指向类似int add(int,int)的函数指针为例
1.先写出函数指针
(int) (*p)(int,int)
2.改成数组
(int) (*p[10])(int,int)//函数指针数组
题目:使用c语言制作简单的计算器.
要求,可以进行加减乘除运算.
//简易计算器
#include <stdio.h>
void menu()//菜单
{printf("作者:初阶牛\n");printf(" 欢迎使用计算器:\n");printf("+-------------------------------+\n");printf("| 1.add 2.sub |\n");printf("| 3.mul 4.div |\n");printf("| 0.退出 |\n");printf("+-------------------------------+\n");
}
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=0, y=0;int input = 1;int ret = 0;do{menu();//打印菜单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");break;default:printf("很遗憾,并没有这个选项:\n");break;}} while (input);return 0;
}
对于上面的代码,我们可以使用函数指针来简化.
感受一下函数的指针的魅力吧!
#include <stdio.h>
void menu()
{printf("作者:初阶牛\n");printf(" 欢迎使用计算器:\n");printf("+-------------------------------+\n");printf("| 1.add 2.sub |\n");printf("| 3.mul 4.div |\n");printf("| 0.退出 |\n");printf("+-------------------------------+\n");
}
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 = 0, y = 0;int input = 1;int ret = 0;int(*arr[5])(int, int)={0,add,sub,mul,div};//使用函数指针数组do{menu();scanf("%d", &input);if (input > 0 && input < 5){printf("输入两个操作数:");scanf("%d %d", &x, &y);ret=arr[input](x,y);//利用数组下标调用相应的函数printf("ret = %d\n", ret);}else if (input == 0){printf("退出计算器\n");break;}else{printf("很遗憾,并没有这个选项:\n");break;}} while (input);return 0;
}
三、函数指针数组指针
函数指针数组指针:
指向一个数组.该数组的成员都是函数指针.
写法:
1.先写出函数指针
char* (*p)(const int, double, char*);
2.改成函数指针数组
char* (*pp[3])(const int, double, char*);
3.最后写成函数指针数组指针char* (*(*ppp)[3])(const int, double, char*);
可以通过函数指针p调用函数,
也可以通过函数指针数组的元素,pp[0]调用函数.
最后,可以通过(*ppp)找到函数指针数组,(*ppp)[0]调用函数.
示例:
char* test(const int a, double b, char* c)
{char* ret = "AAABBBCCCDDD\n";return ret;
}
#include <stdio.h>
int main()
{//函数指针char* (*p)(const int, double, char*)=test;//函数指针数组char* (*pp[3])(const int, double, char*);pp[0] = test;//函数指针数组指针char* (*(*ppp)[3])(const int, double, char*)=&pp;printf("%s", test(0,0,0));printf("%s", p(0, 0, 0));printf("%s", pp[0](0, 0, 0));printf("%s", (*ppp)[0](0, 0, 0));return;
}
四、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
大家还记得冒泡排序吗?
但是冒泡排序只能排序整形,而qsort函数,内部采用快速排序,可以排序各种类型的数据,接下来展示qsort排序部分类型的方法.
示例:qsort函数部分应用
分别将元素比较方法int_cmp和char_cmp的指针(地址) 传给 qsot函数.由qsort函数调用这些比较函数.
#include <stdlib.h>
#include <stdio.h>
int int_cmp(void* e1, void* e2)//整形元素排序方法
{return *(int*)e1 - *(int*)e2;
}
int char_cmp(void* e1, void* e2)//字符型元素排序方法
{return (*(char*)e1) - (*(char*)e2);
}
int main()
{int arr1[10] = { 4,5,1,8,9,2,10,3,7,6 };char arr2[] = "fbadegc";int sz1 = sizeof(arr1) / sizeof(arr1[0]);int sz2 = sizeof(arr2) / sizeof(arr2[0]);qsort(arr1,sz1,sizeof(arr1[0]),int_cmp);for (int i=0; i < sz1; i++){printf("%d ", arr1[i]);}printf("\n");qsort(arr2, sz2, sizeof(arr2[0]), char_cmp);for (int i = 0; i < sz2; i++){printf("%c ", arr2[i]);}printf("%s", arr2);return 0;
}
运行结果:
1 2 3 4 5 6 7 8 9 10
a b c d e f g
qsort函数用冒泡排序的模拟实现,以及各种类型的排序.这周会加更出来的,好文不怕晚.🤣🤣🤣
最后,如果文章对大家有帮助的话,求一波三连吧!
💗💗💗886
相关文章:

带你深入了解c语言指针后续
前言 🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏: 🍔🍟🌯 c语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介:>:介绍c语言中有关指针更深层的知识. 金句分享: ✨在该…...

借助Intune无感知开启Bitlocker
希望使用 Intune 部署 BitLocker,但不知道从哪里开始?这是人们最开始使用 Intune 时最常见的问题之一。在本博客中,你将了解有关使用 Intune 管理 BitLocker 的所有信息,包括建议的设置、BitLocker CSP 在客户端上的工作方式&…...

零基础该如何转行Python工程师?学习路线是什么?
最近1年的主要学习时间,都投资到了 python 数据分析和数据挖掘上面来了,虽然经验并不是十分丰富,但希望也能把自己的经验分享下,最近也好多朋友给我留言,和我聊天,问我python该如何学习,才能少走…...

Go项目(商品微服务-1)
文章目录简介建表protohandler商品小结简介 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的? 主要代码就是 CURD表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里…...

机器学习——集成学习
引言 集成学习:让机器学习效果更好,单个不行,群殴走起。 分类 1. Bagging:训练多个分类器取平均(m代表树的个数)。 2.Boosting(提升算法):从弱学习器开始加,通过加权来进行训练。…...

VS编译系统 实用调试技巧
目录什么是bug?调试是什么?有多重要?debug和release的介绍windows环境调试介绍、一些调试实例如何写出(易于调试)的代码编程常见的错误什么是bug?其实bug在英文翻译中有表示臭虫的含义,因为第一次被发现的导致计算机…...

【华为OD机试模拟题】用 C++ 实现 - GPU 调度(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明GPU 调度题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。...
腾讯前端必会react面试题合集
React-Router的路由有几种模式? React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的…...

Linux搭建SVN服务器,并内网穿透实现公网远程访问
文章目录1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6.2 配置…...

C++STL之list的模拟实现
目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…...

为什么硬件性能监控很重要
当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件:服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...
HTTP缓存
HTTP缓存HTTP缓存引发的一个问题HTTP缓存的作用HTTP缓存的分类强制缓存协商缓存(解决强缓存下资源不更新问题)缓存策略HTTP缓存引发的一个问题 有一次在开发移动端H5项目,UI提了几个UI问题,经过样式调试,android上没有…...

SPI设备树处理过程
SPI设备树处理过程 文章目录SPI设备树处理过程参考资料:一、 spi_device结构体二、 SPI设备树格式2.1 SPI Master2.2 SPI Device2.3 设备树示例三、设备树实例3.1 使用GPIO模拟的SPI控制器3.2 IMX6ULL SPI控制器四、 设备树处理过程致谢参考资料: 内核头…...
数据有哪些重要的作用?
我们正处在科技高速发展的时代,如今互联网已经与我们的生活息息相关,我们每天在互联网产生大量的数据,这些数据散落在网络中看似没有怎么作用,但是这些数据经过系统的处理整合起来确实非常有价值的。 一、 发展大数据技术可以提高…...

spring面试题总结
1、spring是什么? spring是一个轻量级IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用的开发,开发者只需要关注业务需求即可: core container 容器组件 spring context,…...

使用MUI与H5+构建移动端app
前言 通过mui构建APP 效果图: <!DOCTYPE html> <html> <head><meta charset...
第17篇:Java变量总结
目录 1.变量的概念 1.1 变量来源 1.2 计算机中的变量 1.3 变量如何在内存中存储 2.Java变量...

使用51单片机的GPIO输出占空比可调节的PWM波
一、前言 在一些单片机或微控制器中,通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制,是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平,从而实现对电路的控制。 二、什么是PWM波? PWM波&a…...

从产品经理的角度如何提升项目的交付质量?
提高交付质量 ,对于每个IT公司都是永恒的话题。 交付质量其实包含2重意义, 一是交付的高质量(客户角度),即客户的满意度;二是高质量的交付(交付团队的角度),这里是指如何…...

JavaScript BOM【快速掌握知识点】
目录 Window对象的常用属性 语法: Window对象的常用方法 语法: open()和close()方法 History对象 常用属性和方法 示例 Location对象 常用属性 常用方法 Document对象的常用方法 定时函数 超时调用:setTimeout() 间歇调用&…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...