当前位置: 首页 > news >正文

C语言深入理解指针四(17)

文章目录

  • 前言
  • 一、字符指针变量
  • 二、数组指针变量
    • 数组指针变量是什么?
    • 数组指针变量怎么初始化
  • 三、二维数组传参的本质
  • 四、函数指针变量
    • 函数指针变量的创建
    • 函数指针变量的使用
  • 五、两段有趣的代码
    • 代码1
    • 代码2
    • typedef关键字
  • 六、函数指针数组
  • 总结


前言

继续!


一、字符指针变量

  在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用:
在这里插入图片描述
还有一种使用方式如下:
在这里插入图片描述
代码 const char* pstr = “hello bit.”; 特别容易令人误解为把字符串 hello bit. 放到字符指针 pstr 里面,但是本质是把字符串 hello bit. 首字符的地址放到 pstr 中

1.我们可以把字符串想象成一个字符数组,但是这个数组是不能被修改的
2.当常量字符串出现在表达式中的时候,它的值是第一个字符的地址,以下两种写法等价
在这里插入图片描述

有一道关于字符串相关的笔试题,如下:
在这里插入图片描述

请给我你认为的输出

答案是第一个是not,第二个是yes
在这里插入图片描述
我们来思考原因,首先下面那个字符串被const修饰,不会被修改,内容一样,从内存利用率来说,没有必要存多份,只存一份,大家公用即可,而第一个则不然

二、数组指针变量

数组指针变量是什么?

 指针数组:是数组,是存放指针的数组
 整型指针:是指针,指向整型的指针
 数组指针变量:是指针变量,用来存放数组的地址,是能够指向数组的指针变量

为了让你更好地区分,告诉我下面两个分别是什么
在这里插入图片描述
答案是前者是指针数组,后者是数组指针
更准确地说,p1是数组,数组10个元素,每个元素的类型是int*
p2是指针,是指向数组的指针,而所指向的数组有10个元素,每个元素的类型是int

有点绕,你再看看如下:
在这里插入图片描述
通过括号, * 先跟 parr 结合,代表 parr 是一个指针(必须要加括号, [ ] 的优先级大于 * )

数组指针变量怎么初始化

数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?其实就是我们之前所学的&数组名
在这里插入图片描述

如果要存放,那就得存放到数组指针变量当中,即:
int (*parr)[10] = &arr;

通过调试,我们也能看到 &arr 和 p的类型是完全一致的
在这里插入图片描述

三、二维数组传参的本质

  有了数组指针的理解,我们就能够讲以下二维数组传参的本质了
过去我们有一个二维数组需要传参给一个函数的时候,我们是这样写的:
在这里插入图片描述
  那二维数组可以写成指针吗?可以!
这需要我们再次理解一下二维数组,二维数组可以看做是每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组,那么二维数组的首元素就是一行,是个一维数组
在这里插入图片描述
所以,根据数组名是数组首元素的地址这个规划,二维数组的数组名表示的就是第一行的地址,是一维数组的地址,根据上面的例子,第一行的一维数组的类型是int[5],所以第一行的地址的类型就是数组指针类型int (*p)[5],那就意味着二维数组传参本质上也是传递了地址,传递的是第一行在这个一维数组的地址,那么形参也是可以写成指针形式的,如下:
在这里插入图片描述

四、函数指针变量

函数指针变量的创建

  什么是函数指针变量呢?
还是类比,它还是个指针变量,是用来存放函数的地址的变量,未来能通过地址调用函数的
来个测试:
在这里插入图片描述
输出结果如下:
在这里插入图片描述
确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址

跟数组区别的是,函数前面不管加不加取址符 & 都表示函数的地址
int (*pf)(int, int) = &Add; // pf就是函数指针变量
int ret1 = Add(4,9);
int ret2 = (*pf)(4,9); // 调用

函数指针变量的使用

在这里插入图片描述
如上,注意函数指针变量后面的括号加上x,y即(int x, int y)也是可以的

五、两段有趣的代码

代码1

(* ( void ( * )( ) ) 0 )( );
我们发现void(*)()是函数指针类型,而这个类型括号抱起来再跟上0,是强制类型转换,然后再调用
1.把0这个整数值,强制类型转换成一个函数的地址,这个函数没有参数,返回类型是void
2.去调用0地址处的函数!

此段代码出自《C陷阱与缺陷》:在这里插入图片描述

代码2

void (* signal(int, void(*)(int) ) )(int);

这段代码又是什么意思?哈哈,认真思考,这两句代码是本篇最难的部分了~

先来想signal是啥意思,这里没有传参,也没和 * 混在一起,说明它本身是个函数
而一个函数,参数有了,那么还差返回类型,我们把函数名和参数去掉,最后就发现剩下了void( * )(int)
所以,signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该指针指向的函数参数是int,返沪类型为void
signal函数的返回类型是这种类型的void(*)(int)函数指针
该指针指向的函数参数是int,返回类型是void

typedef关键字

typedef关键字是用来简化类型名的
比如说定义一个无符号整数num

unsigned int num; // 太麻烦

这时候,我们可以重命名

typedef unsigned int uint;
typedef int* ptr_t;
typedef int(* parr_t)[5]; // 简化指针类型int (*)[5]
注意,要修改数值指针类型和函数指针类型的时候,名字放里面

对于前面的void (* signal(int, void(*)(int) ) )(int);
我们把 void ( * )(int) 重命名一下
typedef void( * pf_t)(int);

所以上述代码可以简化为
pf_t signal(int, pf_t)(int);

六、函数指针数组

  如果我们要设计一个计算器,要求对整型进行四种运算,显然第一想到的是switch…case语句,可这样代码过于冗杂,我们可以尝试把四种运算都放在一个数组里面,这个数组就是函数指针数组
在这里插入图片描述
跟函数指针变量相比,就是在名字后面加上了[ ],我们成功的将函数的地址放进数组里,这意味着我们可以更加高效的管理函数了!


总结

  回调函数预告,我保证下一章你会感慨函数调用的奇妙!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 国外也开始流行“卷”了吗
  • 抖音ip属地怎么改变到别的城市
  • 使用LSTM(长短期记忆网络)模型预测股票价格的实例分析
  • JsonCpp源码分析——Writer
  • 苹果首款AI手机发布!iPhone 16全新AI功能体验感拉满
  • 【MATLAB】模拟退火算法
  • 软硬链接 动静态库(深入地址空间)
  • Qt:解决player->duration()第一次获取媒体时长为0的问题
  • 在 CentOS 中永久关闭防火墙的步骤
  • mybatis 查询Not Found TableInfoCache
  • Ajax实现一个简单的文件上传进度条
  • 如何将西瓜视频保存到本地(方法)
  • 企业会议室预约管理系统
  • 边缘检测运用
  • 基于单片机实现的的多点分布室内环境监测系统
  • 【技术性】Search知识
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Netty 4.1 源代码学习:线程模型
  • select2 取值 遍历 设置默认值
  • 诡异!React stopPropagation失灵
  • 计算机常识 - 收藏集 - 掘金
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 判断客户端类型,Android,iOS,PC
  • 问题之ssh中Host key verification failed的解决
  • 我这样减少了26.5M Java内存!
  • gunicorn工作原理
  • ​​​​​​​STM32通过SPI硬件读写W25Q64
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​如何使用QGIS制作三维建筑
  • ​数据结构之初始二叉树(3)
  • #pragma pack(1)
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (zt)最盛行的警世狂言(爆笑)
  • (第二周)效能测试
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (六)激光线扫描-三维重建
  • (七)Java对象在Hibernate持久化层的状态
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • (自用)网络编程
  • .equals()到底是什么意思?
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • .Net中间语言BeforeFieldInit
  • :“Failed to access IIS metabase”解决方法
  • @GetMapping和@RequestMapping的区别
  • [AIGC] 解题神器:Python中常用的高级数据结构
  • [Algorithm][动态规划][子序列问题][最长递增子序列][摆动序列]详细讲解
  • [C/C++]数据结构 深入挖掘环形链表问题
  • [c++] 单例模式 + cyberrt TimingWheel 单例分析
  • [error] 17755#0: *58522 readv() failed (104: Connection reset by peer) while reading upstream
  • [FFmpeg学习]从视频中获取图片