深入理解指针3
hello,各位小伙伴,本篇文章跟大家一起继续深入学习指针,感谢大家对我上一篇的支持,如有什么问题,还请多多指教
如果本篇文章对你有帮助,还请各位点点赞!!!
话不多说,进入正题
1. 字符指针变量
一般,我们有这两种方式来创建字符指针,但是我们看到代码2中的字符指针用 const 修饰了,也就是*pstr不能改变,也就是字符串不能修改。因为代码2中字符指针存储的是常量字符串,常量字符串是不能修改的。
这里有一个要注意的点:在代码2中,字符串指针pstr存的不是整个字符串,而是字符串首字符的地址。看图解:
所以,pstr存的是h的地址
《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:
#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char *str3 = "hello bit.";const char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
想必通过上面的讲解,小伙伴们都知道答案了吧:

这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域, 当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是用相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
2. 数组指针变量
2.1 数组指针变量是什么?
首先我们要搞明白的是:数组指针变量是指针变量。举个例子:红色苹果是苹果,因为红色是形容词;所以数组指针变量中的数组就是形容词,用来修饰指针变量。
我们学过整型指针变量、还有刚那个字符指针变量:
整型指针变量:int * pint;存放的是整形变量的地址,能够指向整形数据的指针。
浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
同理得:数组指针变量:存放的应该是数组的地址,能够指向数组的指针变量。
我们来猜猜哪一个是数组指针变量
int *p1[10];
int (*p2)[10];
答案是:p2
首先,[ ]的优先级比 * 更高,所以[ ]会优先与p1结合,也就是p1是一个数组,存放的是int *元素(也就存放的是:整型指针),所以p1为指针数组
那么p2有()括着*p2,也就是p2是一个指针,指向后面的[10](也就是指向数组,大小为10个整型的数组),而前面的int则是表示数组里存的元素为整型,所以p2为数组指针变量
这⾥要注意:[]的优先级要⾼于*号的,所以必须加上()来保证p先和*结合。
2.2 数组指针变量怎么初始化
知道数组指针变量存的是数组的地址,那怎么存呢?
在深入理解指针2有讲过:&数组名
int arr[10] = {0};
&arr;//取得arr的地址
那么同样的,指针指向数组:
int arr[10] = {0};
int (*p)[10] = &arr;

可以看到&arr和p的类型是完全一样的
数组指针类型解析:
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组的元素个数
| p是数组指针变量名
p指向的数组的元素类型
3. ⼆维数组传参的本质
有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。 过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:
#include <stdio.h>
void test(int a[3][5], int r, int c)
{int i = 0;int j = 0;
for(i=0; i<r; i++){for(j=0; j<c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};test(arr, 3, 5);return 0;
}
我们知道二维数组就是一维数组里存放的是一维数组,那么一维数组的传参我们是学过的呀:
就是把数组的首元素地址传过去,那么二维数组首元素不就是一维数组了吗,那就意味着⼆维数组传参本质上也是传递了地址,传递的是第一个一维数组的地址,一维数组的地址类型:就是数组指针类型----int(*)[5] ,那么形参也是可以写成指针形式的
#include <stdio.h>
void test(int (*p)[5], int r, int c)
{int i = 0;
int j = 0;for(i=0; i<r; i++){for(j=0; j<c; j++){printf("%d ", *(*(p+i)+j));// *(p+i) :就是二维数组里的元素---一维数组} // *(*(p+i)+j)) :就是一维数组里的元素---整型变量printf("\n");}
}
int main()
{int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};test(arr, 3, 5);return 0;
}
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
不过写成数组形式更容易让人理解罢了
4. 函数指针变量
4.1 函数指针变量的创建
是不是和数组指针变量名字上有点相似,对的,函数指针变量也是指针,存放的当然是函数啦
为了方便后面的理解,我们下来看一段代码:
#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
结果如下:

嗯?!&test和test在printf里打印地址的效果竟然一样,这种操作是不是似曾相识,没错,我们的数组也是一样的,等等!函数竟然也有地址,对,函数也有地址,函数名就是函数的地址
那我们把函数的地址存起来,不就是创建了函数指针变量了嘛,函数指针变量的写法其实和数组指针非常类似,看:
void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;//都表示test函数的地址
int Add(int x, int y)
{return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的
我们来分析一下这个指针类型的写法:
int (*pf3) (int x, int y)
*:表示pf3是一个指针
pf3:表示指针的变量名
*pf3后面跟着的 ( ) :表示指针指向一个函数
该 ( ) 里面的int x,int y:表示这个函数所需传参的类型以及传参的个数
int:表示这个函数的返回类型为整型(int)

4.2 函数指针变量的使用
既然我都存下了函数的地址,那我肯定是要用的,所以通过函数指针调用指针指向的函数。
#include <stdio.h>
int Add(int x, int y)
{return x+y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}

发现pf3指针不需要解引用也可以调用函数

得出结论:pf3和test是一样的
我们来看看有趣的代码
代码1
(*(void (*)())0)();
void (*)():就是一个函数指针类型
(void (*)())0:就是将0强制转换为该函数指针类型
*(void (*)())0:就是将0解引用,也就是函数的名字
(*(void (*)())0)():就是一个函数
代码2
void (*signal(int , void(*)(int)))(int);
这是一个函数声明,用于声明一个名为`signal`的函数,该函数接受两个参数:一个整数类型的参数和一个指向函数的指针,该函数的参数是整数类型且返回值为空。
函数`signal`的返回值是一个指向函数的指针,该函数有一个整数类型的参数和返回值为空。
通常,该函数用于注册信号处理程序,以处理在程序运行过程中接收到的各种信号。它允许用户指定对特定信号的处理方式,当接收到相应的信号时,调用相应的函数来处理。
请注意,这只是一个函数声明,并没有给出具体的函数实现。在使用`signal`函数之前,需要进行函数定义并提供相应的信号处理程序来实现具体的功能。
4.3.1 typedef关键字
⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:
typedef unsigned int uint;
//将unsigned int 重命名为uint
如果是指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:
typedef int* ptr_t;
但是对于数组指针和函数指针稍微有点区别: ⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
那么修改名字后,代码可以这样写:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
5. 函数指针数组
想必小伙伴们都知道函数指针数组是一个数组,用来存放函数指针变量的
我们来猜猜哪一个是函数指针数组:
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];
答案是:parr1
parr1[3]:表示函数指针数组是一个数组
parr1[3]前的 * :表示该数组存的是函数指针(也就是指针嘛)
(*parr1[3])后面的( ):表示该指针指向函数,当然啦,( ) 里面的参数就和函数指针里的意思一样
前面的int :表示该函数的返回类型
总的来说:该数组的内容是int (*)() 类型的函数指针
好啦,本篇文章对于指针就讲到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!
如你喜欢,点点赞就是对我的支持,感谢感谢!!!
相关文章:
深入理解指针3
hello,各位小伙伴,本篇文章跟大家一起继续深入学习指针,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 如果本篇文章对你有帮助,还请各位点点赞!!! 话不多说&am…...
大数据环境下在线考试系统安全策略研究
摘 要 随着云计算、物联网、电子商务、企业信息化等的飞速发展,以及智能终端和各种检测、感应设备的普及和建设,全球逐渐进入信息化、网络化,由此产生了指数爆炸般的数据增长,一个大规模生产、分享和应用的数据的时代正在开启&am…...
Python中程序的异常处理
Python程序一般对输入有一定要求,担当实际输入不满足程序要求时,可能会产生程序的运行错误。Python语言使用的保留太容易try和except进行异常处理! try: 语句块1 except: 语句块2 语句块1是正常执行的程序内容,当这个语句块发生异…...
有趣的代码——有故事背景的程序设计3
这篇文章再和大家分享一些有“背景”的程序设计,希望能够让大家学到知识的同时,对编程学习更感兴趣,更能在这条路上坚定地走下去。 目录 1.幻方问题 2.用函数打印九九乘法表 3.鸡兔同笼问题 4.字数统计 5.简单选择排序 1.幻方问题 幻方又…...
聚观早报 |国行PS5轻薄版开售;岚图汽车11月交付7006辆
【聚观365】12月2日消息 国行PS5轻薄版开售 岚图汽车11月交付7006辆 比亚迪推出12月限时优惠 特斯拉正式交付首批Cybertruck 昆仑万维发布「天工 SkyAgents」平台 国行PS5轻薄版开售 索尼最新的PlayStation5主机(CFI-2000型号组-轻薄版)国行版本正…...
Kafka 保证消息消费全局顺序性
当有消息被生产出来的时候,如果没有指定分区或者指定 key ,那么消费会按照【轮询】的方式均匀地分配到所有可用分区中,但不一定按照分区顺序来分配 我们知道,在 Kafka 中消费者可以订阅一个或多个主题,并被分配一个或多…...
3分钟在CentOS 7上离线安装Docker
在CentOS 7上离线安装Docker的详细步骤如下: 环境检查和准备 检查内核版本:Docker要求系统为64位且内核版本至少为3.10。使用命令uname -r查看内核版本。 检查CentOS版本:通过命令cat /etc/redhat-release查看版本信息。 更新yum包࿰…...
GaussDB数据库SQL系列-触发器
目录 一、前言 二、触发器概念 三、GaussDB数据库中的触发器 1、语法格式 2、创建步骤 3、注意事项 4、附:表和视图上支持的触发器种类 四、GaussDB数据库中的示例 示例一、在GaussDB数据库中创建一个触发器,以便在插入新记录时自动将记录的创建…...
网工学习10-IP地址
一、IP地址概念 IP地址是一个32位的二进制数,它由网络ID和主机ID两部份组成,用来在网络中唯一的标识的一台计算机。网络ID用来标识计算机所处的网段;主机ID用来标识计算机在网段中的位置。IP地址通常用4组3位十进制数表示,中间用…...
二百零八、Hive——HiveSQL异常:Select查询数据正常,但SQL语句加上group by查询数据为空
一、目的 在HiveSQL的DWD层中,需要对原始数据进行去重在内的清洗,结果一开始其他数据类型的清洗工作都正常,直到碰到转向比数据。 一般的SQL查询有数据,但是加上group by以后就没数据; 一般的SQL查询有数据…...
Docker—共享应用程序
现在您已经构建了一个映像,可以共享它。要共享Docker映像,您必须使用Docker注册表。默认注册表是Docker Hub,是您使用的所有图像的来源。 Docker ID(Docker标识) Docker ID允许您访问Docker Hub,这是世界上…...
Linux横向移动
Linux横向移动 主机存活探测 shell for i in 192.168.111.{1..254}; do if ping -c 3 -w 3 $i &>/dev/null; then echo $i is alived; fi; done 或者 for k in $( seq 1 255);do ping -c 1 192.168.1.$k|grep "ttl"|awk -F "[ :]" {print $4}; d…...
Ubuntu 20.0 + mysql 8.0 用户和密码修改
第一步 下载(简单,注意联网)Ubuntu 终端输入以下两行命令 (1) 数据库的服务端及客户端数据库的开发软件包 sudo apt-get install mysql-server mysql-client (2) 数据库的开发软件包 sudo apt-get install libmysqlclient-dev 第二步 查看是否安装成功 …...
看懂lscpu的输出
文章目录 1. lscpu1.1 Architecture1.2 逻辑核心数1.3 缓存1.4 CPU型号1.5 NUMA架构1.5.1 CPU多核架构1.5.2 多CPU Socket架构 2. cat /proc/cpuinfo2.1 关键字段 1. lscpu 通过lscpu查看当前系统的CPU信息。 [hadoopserver3 ~]$ lscpuArchitecture: x86_64 …...
RoPE旋转位置编码浅析
RoPE旋转位置编码浅析 本文介绍了旋转位置编码RoPE在大模型中的广泛应用,包括Llama、Mistral 7B、Baichuan、ChatGLM、Qwen、…等。由于计算资源限制,大模型通常在较小的上下文长度中进行训练,导致在推理超出预训练长度时性能显著下降。为了解决这个问题,涌现了许多基于Ro…...
在 SQL Server 中备份和恢复数据库的最佳方法
在SQL Server中,创建备份和执行还原操作对于确保数据完整性、灾难恢复和数据库维护至关重要。以下是备份和恢复过程的概述: 方法 1. 使用 SQL Server Management Studio (SSMS) 备份和还原数据库 按照 SSMS 步骤备份 SQL 数据库 打开 SSMS 并连接到您…...
Java8 根据自定义属性去重
一、需求背景 有一个List集合,里面有n个Answer成员,成员种有m个属性,即: List<Answer> answers ...,现在我需要根据Answer的某一个属性对List去重(属性不固定),应该如何实现呢? 二、解决办法 需要满足上述需求,并不难,我们可以使用Java8提供的…...
Netty网络编程
入门案例 1、服务器端代码 public class HelloServer {public static void main(String[] args) {// 1、启动器,负责装配netty组件,启动服务器new ServerBootstrap()// 2、创建 NioEventLoopGroup,可以简单理解为 线程池 Selector.group(n…...
层三交换机解析(Layer 3 Switch)层3交换机
文章目录 层三交换机解析1. 概述1.1 什么是层三交换机1.2 层三交换机与路由器、二层交换机的比较 2. 层三交换机的工作原理2.1 基于MAC地址的转发2.2 基于IP地址的转发 3. 层三交换机的配置4. 常见问题与解答4.1 我应该使用路由器还是层三交换机?4.2 层三交换机可以…...
expect自动化交互
目录 1. expect作用: 2. expect语言用法: 3. 实例 1. expect作用: 是建立在tcl语言基础上的一个工具,常被用于进行自动化控制和测试,解决shell脚本中交互的相关问题。 2. expect语言用法: spawn开启免…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
