C语言:数组转换指针的时机

1、指针数组

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组,指针数组的定义形式一般为:

dataType *arrayName[length];

[ ]的优先级高于*,该定义形式应该理解为:

dataType *(arrayName[length]);

括号里说明arryName是一个数组,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子:

1. #include <stdio.h> 
2. int main(){ 
3. int a = 16, b = 932, c = 100; 
4. //定义一个指针数组
5. int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *parr[] 
6. //定义一个指向指针数组的指针
7. int **parr = arr; 
8. printf("%d, %d, %d\\n", *arr[0], *arr[1], *arr[2]); 
9. printf("%d, %d, %d\\n", **(parr+0), **(parr+1), **(parr+2)); 
10.
11. return 0; 
12. }

运行结果: 16, 932, 100 16, 932, 100

arr是一个指针数组,它包含了3个元素,每个元素都是一个指针,在定义arr的同时,我们使用变量a、b、c的地址对它进行初始化,这和普通数组多么地类似。

parr是指向数组arr的指针,确切的说是指向第0个元素的指针,它的定义形式应该理解为 int *(parr)括号中的表示 parr 是一个指针,括号外面的 int *表示 parr 指向的数据的类型。arr 第 0 个元素的类型为 int *,所以在定义 parr 时要加两个 *。

第一个 printf() 语句中,arr[i] 表示获取第 i 个元素的值,该元素是一个指针,还需要在前面增加一个 * 才能取得它指向的数据,也即 *arr[i] 的形式。

第二个 printf() 语句中,parr+i 表示第 i 个元素的地址,*(parr+i) 表示获取第 i 个元素的值(该元素是一个指针),**(parr+i) 表示获取第 i 个元素指向的数据。

指针数组还可以和字符串数组结合使用,请看下面的例子:

1. #include <stdio.h> 
2. int main(){ 
3. char *str[3] = { 
4. "c string", 
5. "c string", 
6. "c string" 
7. }; 
8. printf("%s\\n%s\\n%s\\n", str[0], str[1], str[2]); 
9. return 0; 
10. }
c string
c string
c string

需要注意的是,字符数组 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。

也只有当指针数组中每个元素的类型都是 char *时,才能像上面那样给指针数组赋值,其他类型不行。

为了便于理解,可以将上面的字符串数组改成下面的形式,它们都是等价的。

1. #include <stdio.h> 
2. int main(){ 
3. char *str0 = "c string"; 
4. char *str1 = "c string"; 
5. char *str2 = "c string"; 
6. char *str[3] = {str0, str1, str2}; 
7. printf("%s\\n%s\\n%s\\n", str[0], str[1], str[2]); 
8. return 0; 
9. }

数组名的本意是表示一组数据的集合,它和普通变量一样,都是用来指代一块内存,但在使用过程中,数组名有时候会转换为指向数据集合的指针(地址),而不是表示数据集合本身,这在前面的例子中已经被多次证实。

数据集合包含了多份数据,直接使用一个集合没有明确的含义,将数组名转换为指向数组的指针后,可以很容易地访问其中任何一份数据,是用时的语义更加准确。

c语言标准规定,当数组名作为数组定义的表示符(也就是定义或声明数组时)、sizeof或者&的操作数时,它才表示整个数组本身,在其他的表达式中,数组名会被转换为指向第0个元素的指针(地址)

数组和指针的关系就像诗与词的关系,它们都是一种文学形式,有不少共同之处,但在实际的表现手法上又各有特色。

再谈数组下标[ ]

C 语言标准还规定,数组下标与指针的偏移量相同。通俗地理解,就是对数组下标的引用总是可以写成“一个指向数组的起始地址的指针加上偏移量”。假设现在有一个数组 a 和指针变量 p,它们的定义形式为:

  1. int a = {1, 2, 3, 4, 5}, *p, i = 2;

对数组的引用 a[i] 在编译时总是被编译器改写成*(a+i)的形式,C 语言标准也要求编译器必须具备这种行为。

取下标操作符[ ]是建立在指针的基础上,它的作用是使一个指针和一个整数相加,产生出一个新的指针,然后从这个新指针(新地址)上取得数据;假设指针的类型为 T *,所产生的结果的类型就是 T。

取下标操作符的两个操作数是可以交换的,它并不在意操作数的先后顺序,就像在加法中 3+5 和 5+3 并没有什么不一样。以上面的数组 a 为例,如果希望访问第 3 个元素,那么可以写作 a[3],也可以写作 3[a],这两种形式都是正确的,只不过后面的形式从不曾使用,它除了可以把初学者搞晕之外,实在没有什么实际的意义。

a[3] 等价于 *(a + 3),3[a] 等价于 *(3 + a),仅仅是把加法的两个操作数调换了位置。

使用下标时,编译器会自动把下标的步长调整到数组元素的大小。数组 a 中每个元素都是 int 类型,长度为 4个字节,那么 a[i+1]和 a[i]在内存中的距离是 4(而不是 1)。

数组作函数参数

C 语言标准规定,作为“类型的数组”的形参应该调整为“类型的指针”。在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第 0 个元素的指针形式。编译器只向函数传递数组的地址,而不是整个数组的拷贝。

这种隐式转换意味着下面三种形式的函数定义是完全等价的:

1. void func(int *parr){ ...... }
2. void func(int arr[]){ ...... }
3. void func(int arr[5]){ ...... }

在函数内部,arr 会被转换成一个指针变量,编译器为 arr 分配 4 个字节的内存,用 sizeof(arr) 求得的是指针变量的长度,而不是数组长度。要想在函数内部获得数组长度必须额外增加一个参数,在调用函数之前求得数组长度,这在《C 语言指针变量作为函数参数》一节已经重点强调过。

参数传递是一次赋值的过程,赋值也是一个表达式,函数调用时不管传递的是数组名还是数组指针,效果都是一样的,相当于给一个指针变量赋值。

把作为形参的数组和指针等同起来是出于效率方面的考虑。数组是若干类型相同的数据集合,数据的数目没有限制,可能只有几个,也可能成千上万,如果要传递整个数组,无论在时间还是内存空间上的开销都可能非常大。而绝大部分情况下,我们其实并不需要整个数组的拷贝,我们指向告诉函数在那一刻对哪个特定的数组感兴趣。

关于数组和指针可交换性的总结

1)用 a[i] 这样的形式对数组进行访问总是会被编译器改写成(或者说解释为)像 *(a+i) 这样的指针形式。

2)指针始终是指针,它绝不可以改写成数组。你可以用下标形式访问指针,一般都是指针作为函数参数时,而且你知道实际传递给函数的是一个数组。

3)在特定的环境中,也就是数组作为函数形参,也只有这种情况,一个数组可以看做是一个指针。作为函数形参的数组始终会被编译器修改成指向数组第一个元素的指针。

4)当希望向函数传递数组时,可以把函数参数定义为数组形式(可以指定长度也可以不指定长度),也可以定义为指针。不管哪种形式,在函数内部都要作为指针变量对待。

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

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

相关文章

UE5 DownloadImage加载jpg失败的解决方法

DownloadImage加载jpg失败的解决方法 现象解决方案具体方法 现象 用UE自带的 DownloadImage 无法下载成功&#xff0c;从 failure 引脚出来。 接入一个由监控器自动保存起的图像&#xff0c;有些可以正常加载成功&#xff0c;有些无法加载成功。 经调查问题出现在&#xff0c;…

使用 helm 部署 gitlab

一、下载 Gitlab chart 进入 artifacthub 官网 选择你想要的版本&#xff08;我选择的chart版本是 8.4.0 , gitlab 版本是17.4.0 &#xff09; 进入到控制台&#xff0c;添加helm仓库 如果你想不改任何配置&#xff0c;你可以执行安装命令&#xff0c;等待安装即可helm instal…

FreeRTOS信号量(一)

目录 什么是信号量&#xff1f; 1.信号量简介 2.二值信号量 2.1二值信号量简介 1. 首先&#xff0c;创建时&#xff0c;二值信号量默认无效 2. 之后中断释放信号量 3.信号量获取成功 4、任务再次进入阻塞态 2.2 创建二值信号量 1、函数vSemaphoreCreateBinary () 2、…

51单片机-独立按键与数码管联动

独立键盘和矩阵键盘检测原理及实现 键盘的分类&#xff1a;编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如&#xff1a;计算机键盘。靠软件编程识别的称为非编码键盘&#xff1b;在单片机组成…

springboot课程答疑系统(代码+数据库+LW)

摘要 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个专门适应师生交流形式的网站。本文介绍了课程答疑系统的开发全过程。通过分析企业对于课程答疑系统的需求&#xff0c;创建了一个计算机管理课程答疑系统的方案。文章…

解锁业务成功:大数据和 AI 如何协作以释放战略洞察

在当今这个数据主导的时代&#xff0c;大数据与AI的协同作用对于寻求竞争优势的组织而言愈发关键。大数据以其庞大的数据量、多样化的数据类型以及高速的数据生成能力&#xff0c;为AI算法提供了丰富的原材料&#xff0c;助力其挖掘出有价值的洞见&#xff0c;推动明智决策的制…

24.UE5枚举,怪物分类,龙卷风技能

2-26 枚举、怪物分类、龙旋风技能、掉落概率_哔哩哔哩_bilibili 目录 1.枚举 1.1枚举类型的创建 1.2 将枚举类型绑定到怪物蓝图上 1.3枚举类型的使用 1.3.1创建新的掉落物 1.3.2更改怪物掉落逻辑 2.龙卷风技能 2.1输入映射 2.2龙卷风发射物的创建 2.3龙卷风伤害逻辑…

故障字故障码 简单介绍

一、故障字 1.1故障字的概念 故障字&#xff08;Fault Word&#xff09;是一种常用的技术术语&#xff0c;主要应用在工业控制、嵌入式系统和通信领域&#xff0c;用于表示系统状态或故障信息。它是一个以位为单位的编码方式&#xff0c;每个位&#xff08;bit&#xff09;对应…

鸿蒙系统ubuntu开发环境搭建

在RISC-V等平台移植鸿蒙系统OpenHarmony&#xff0c;需要使用linux环境进行代码的编译&#xff0c;为兼顾日常办公需要&#xff0c;可采用WindowsUbuntu虚拟机的混合开发的环境&#xff0c;通过网络及文件夹共享&#xff0c;在主机和虚拟机之间共享文件数据。 工具准备&#x…

二叉树oj题解析

二叉树 二叉树的最近公共祖先什么是最近公共祖先&#xff1f;leetcode中求二叉树中最近公共祖先解题1.解题2. 根据二叉树创建字符串 二叉树的最近公共祖先 什么是最近公共祖先&#xff1f; 最近的公共祖先指的是这一棵树中两个节点中深度最大的且公共的祖先节点就是最近祖先节…

优先算法 —— 双指针系列 - 移动零

1. 移动零 题目链接&#xff1a; 283. 移动零 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/move-zeroes/description/ 2. 算法原理 其实像移动零这种类型的题目都有一个名字叫做数组划分&#xff08;数组分块&#xff09;&#xff0c;就是说先给一个…

C语言——数组逐元素操作练习

定义一个能容纳10个元素的整形数组a&#xff0c;从键盘读取9个整数存放到前9个数组元素中。 一. 从键盘读取一个整数n和位置p(0<p<8)&#xff0c;插入n到数组a中&#xff0c;插入位置&#xff1a;下标p。要求插入点及后续的数组元素都要后移动。 代码如下&#xff1a; …

【ArcGISPro】根据yaml构建原始Pro的conda环境

使用场景 我们不小心把原始arcgispro-py3的conda环境破坏了,我们就可以使用以下方法进行修复 查找文件 在arcgis目录下找到yaml文件 如果没找到请复制以下内容到新的yaml文件 channels: - esri - defaults dependencies: - anyio=4.2.0=py311haa95532_0 - appdirs=1.4.4=p…

解决IDEA报包不存在,但实际存在的问题

前言 最近在把一个亿老项目交割给同事&#xff0c;同事在导入项目运行时遇到IDEA报包不存在&#xff0c;但实际存在的问题&#xff0c;最终通过以下方式解决 现象 在IDEA里启动运行项目&#xff0c;报某个类有问题&#xff0c;引入的包不存在。 点击这个引入的包&#xff0c;可…

使用uniapp编写APP的文件上传

使用uniapp插件文件选择、文件上传组件&#xff08;图片&#xff0c;视频&#xff0c;文件等&#xff09; - DCloud 插件市场 实用效果&#xff1a; 缺陷是只能一个一个单独上传

图算法 | 3、图分析与数据科学

图分析(Graph Analytics)在本质上是对图数据的处理与分析&#xff0c;其过程可以概括为图计算。 而图计算的范畴不仅包含数据的计算或分析&#xff0c;还包含元数据管理、模式管理、数据建模、数据清洗、转换、加载、治理、图分析与计算等一系列操作。 或许我们用大数据生命周…

66 mysql 的 表自增长锁

前言 mysql 的表锁之 AUTO_INC, 是我们自增长的时候做并发控制的锁 主要是用于 自增长生成新的 id 的时候的控制 在前面的文档中, 我们又看到 mysql 这边自增长的处理的相关的大概脉络 但是 对于一些 并发控制的细节, 我们当时 应该是直接忽略掉了 我们这里就来看一下…

Elasticsearch向量搜索:从语义搜索到图搜图只有一步之遥

续 上集说到语义搜索&#xff0c;这集接着玩一下图搜图&#xff0c;这种场景在电商中很常见——拍照搜商品。图搜图实现非常类似语义搜索&#xff0c;代码逻辑结构都很类似… 开搞 还是老地方modelscope找个Vision Transformer模型&#xff0c;这里选用vit-base-patch16-224…

HCIA笔记3--TCP-UDP-交换机工作原理

1. tcp协议 可靠的连接 1.1 报文格式 1.2 三次握手 1.3 四次挥手 为什么TIME_WAIT需要2MSL的等待时间&#xff1f; &#xff08;a&#xff09; 为了实现可靠的关闭 &#xff08;b&#xff09;为了让过期的报文在网络上消失 对于(a), 假设host发给server的last ack丢了。 ser…

docker搭建私有仓库,实现镜像的推送和拉取

1.拉取docker仓库镜像 docker pull registry 2.启动registry容器 docker run -d registry 3.查看当前仓库中存在的镜像&#xff08;一&#xff09; curl -XGET http://192.168.111.162: 5000/v2/_catalog 192.168.111.162 部署docker仓库宿主机的ip 5000 部署docker仓库映射到宿…