C语言数组指针(指向数组的指针)详解

news2024/11/16 18:02:23

数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。以int arr[] = { 99, 15, 100, 888, 252 };为例,该数组在内存中的分布如下图所示:

定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第 0 个元素。在C语言中,我们将第 0 个元素的地址称为数组的首地址。以上面的数组为例,下图是 arr 的指向:

数组名的本意是表示整个数组,也就是表示多份数据的集合,但在使用过程中经常会转换为指向数组第 0 个元素的指针,所以上面使用了“认为”一词,表示数组名和数组首地址并不总是等价。初学者可以暂时忽略这个细节,把数组名当做指向第 0 个元素的指针使用即可。

下面的例子演示了如何以指针的方式遍历数组元素:

#include<stdio.h>

intmain(){
int arr[]={99,15,100,888,252};
int len =sizeof(arr)/sizeof(int);//求数组长度
int i;
for(i=0; i<len; i++){
printf("%d  ",*(arr+i));//*(arr+i)等价于arr[i]
}
printf("\n");
return0;
}

运行结果:

99 15 100 888 252

第 5 行代码用来求数组的长度,sizeof(arr) 会获得整个数组所占用的字节数,sizeof(int) 会获得一个数组元素所占用的字节数,它们相除的结果就是数组包含的元素个数,也即数组长度。

第 8 行代码中我们使用了*(arr+i)这个表达式,arr 是数组名,指向数组的第 0 个元素,表示数组首地址, arr+i 指向数组的第 i 个元素,*(arr+i) 表示取第 i 个元素的数据,它等价于 arr[i]。

arr 是int*类型的指针,每次加 1 时它自身的值会增加 sizeof(int),加 i 时自身的值会增加 sizeof(int) * i。

我们也可以定义一个指向数组的指针,例如:

int arr[]={99,15,100,888,252};
int*p = arr;

arr 本身就是一个指针,可以直接赋值给指针变量 p。arr 是数组第 0 个元素的地址,所以int *p = arr;也可以写作int *p = &arr[0];。也就是说,arr、p、&arr[0] 这三种写法都是等价的,它们都指向数组第 0 个元素,或者说指向数组的开头。

再强调一遍,“arr 本身就是一个指针”这种表述并不准确,严格来说应该是“arr 被转换成了一个指针”。

如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。

数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关,上面的例子中,p 指向的数组元素是 int 类型,所以 p 的类型必须也是int *。

反过来想,p 并不知道它指向的是一个数组,p 只知道它指向的是一个整数,究竟如何使用 p 取决于程序员的编码。

更改上面的代码,使用数组指针来遍历数组元素:

#include<stdio.h>

intmain(){
int arr[]={99,15,100,888,252};
int i,*p = arr, len =sizeof(arr)/sizeof(int);

for(i=0; i<len; i++){
printf("%d  ",*(p+i));
}
printf("\n");
return0;
}

数组在内存中只是数组元素的简单排列,没有开始和结束标志,在求数组的长度时不能使用sizeof(p) / sizeof(int),因为 p 只是一个指向 int 类型的指针,编译器并不知道它指向的到底是一个整数还是一系列整数(数组),所以 sizeof(p) 求得的是 p 这个指针变量本身所占用的字节数,而不是整个数组占用的字节数。

也就是说,根据数组指针不能逆推出整个数组元素的个数,以及数组从哪里开始、到哪里结束等信息。不像字符串,数组本身也没有特定的结束标志,如果不知道数组的长度,那么就无法遍历整个数组。

上节我们讲到,对指针变量进行加法和减法运算时,是根据数据类型的长度来计算的。如果一个指针变量 p 指向了数组的开头,那么 p+i 就指向数组的第 i 个元素;如果 p 指向了数组的第 n 个元素,那么 p+i 就是指向第 n+i 个元素;而不管 p 指向了数组的第几个元素,p+1 总是指向下一个元素,p-1 也总是指向上一个元素。

更改上面的代码,让 p 指向数组中的第二个元素:

#include<stdio.h>

intmain(){
int arr[]={99,15,100,888,252};
int*p =&arr[2];//也可以写作 int *p = arr + 2;

printf("%d, %d, %d, %d, %d\n",*(p-2),*(p-1),*p,*(p+1),*(p+2));
return0;
}

运行结果:

99, 15, 100, 888, 252

引入数组指针后,我们就有两种方案来访问数组元素了,一种是使用下标,另外一种是使用指针。

1) 使用下标

也就是采用 arr[i] 的形式访问数组元素。如果 p 是指向数组 arr 的指针,那么也可以使用 p[i] 来访问数组元素,它等价于 arr[i]。

2) 使用指针

也就是使用 *(p+i) 的形式访问数组元素。另外数组名本身也是指针,也可以使用 *(arr+i) 来访问数组元素,它等价于 *(p+i)。

不管是数组名还是数组指针,都可以使用上面的两种方式来访问数组元素。不同的是,数组名是常量,它的值不能改变,而数组指针是变量(除非特别指明它是常量),它的值可以任意改变。也就是说,数组名只能指向数组的开头,而数组指针可以先指向数组开头,再指向其他元素。

更改上面的代码,借助自增运算符来遍历数组元素:

#include<stdio.h>

intmain(){
int arr[]={99,15,100,888,252};
int i,*p = arr, len =sizeof(arr)/sizeof(int);

for(i=0; i<len; i++){
printf("%d  ",*p++);
}
printf("\n");
return0;
}

运行结果:

99 15 100 888 252

第 8 行代码中,*p++ 应该理解为 *(p++),每次循环都会改变 p 的值(p++ 使得 p 自身的值增加),以使 p 指向下一个数组元素。该语句不能写为 *arr++,因为 arr 是常量,而 arr++ 会改变它的值,这显然是错误的。

关于数组指针的谜题

假设 p 是指向数组 arr 中第 n 个元素的指针,那么 *p++、*++p、(*p)++ 分别是什么意思呢?

*p++ 等价于 *(p++),表示先取得第 n 个元素的值,再将 p 指向下一个元素,上面已经进行了详细讲解。

*++p 等价于 *(++p),会先进行 ++p 运算,使得 p 的值增加,指向下一个元素,整体上相当于 *(p+1),所以会获得第 n+1 个数组元素的值。

(*p)++ 就非常简单了,会先取得第 n 个元素的值,再对该元素的值加 1。假设 p 指向第 0 个元素,并且第 0 个元素的值为 99,执行完该语句后,第 0 个元素的值就会变为 100。

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

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

相关文章

Homekit智能家居DIY-智能触摸面板开关

触摸开关&#xff0c;即通过触摸方式控制的墙壁开关&#xff0c;其感官场景如同我们的触屏手机&#xff0c;只需手指轻轻一点即可达到控制电器的目的&#xff0c;随着人们生活品质的提高&#xff0c;触摸开关将逐渐将换代传统机械按键开关。 触摸开关控制原理 触摸开关我们把…

VL6 多功能数据处理器

一、题目根据指示信号select的不同&#xff0c;对输入信号a,b实现不同的运算。输入信号a,b为8bit有符号数&#xff0c;当select信号为0&#xff0c;输出a&#xff1b;当select信号为1&#xff0c;输出b&#xff1b;当select信号为2&#xff0c;输出ab&#xff1b;当select信号为…

5、排序与分页

文章目录1 排序数据1.1 排序规则1.2 单列排序1.3 多列排序2 分页2.1 背景2.2 实现规则2.3 拓展尚硅谷MySQL数据库教程-讲师&#xff1a;宋红康 我们缺乏的不是知识&#xff0c;而是学而不厌的态度 1 排序数据 1.1 排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&…

MySQL安装教程(windows 64位)详细教程

1.确保电脑为64位系统 2.国外官网下载地址&#xff1a;MySQL : Download MySQL Community Server 2.1.点击下载之后&#xff0c;可以选择注册Oracle账号&#xff0c;也可以跳过直接下载 2.2.国内下载网址&#xff1a;Index of /mysql/MySQL-8.0/ 2.3.下载完成后解压到某一个文…

Qt 播放音频文件的几种方式

文章目录摘要1 QMediaPlayer1.1 播放音频文件1.2 播放视频文件1.3 我遇到的问题2 QSound3 QSoundEffect4 QAudioOutput关键字&#xff1a; Qt、 QSound、 QSoundEffect、 QMediaPlayer、 multimedia摘要 这篇文章至少拖了有一两个月了&#xff0c;这不阳了&#xff0c;在家实…

Linux的tree命令原来用处那么大,涨知识了!

大家应该熟悉或了解 Linux 中的目录结果&#xff0c;它就像树的根。这正是 tree 命令的概念。它以树状方式显示当前目录及其子目录的内容。 在使用 tree 命令之前&#xff0c;需要首先安装。 安装 tree 大多数 Linux 发行版中都没有预安装 tree 命令&#xff0c;但是可以在官…

XSS Game通关教程

12.XSS Game通关教程 1、Ma Spaghet! 审查源码 一个不安全的方式&#xff0c;直接get传输somebody 输入123看看位置 ?somebody123 如下 用下input标签即可 <input onmouseoveralert(1)>当鼠标移动到输入框时触发弹窗 2、Jefff 传入的jeff在eval里&#xff0c;那就…

MySQL(二)

schema与数据类型优化 数据类型的优化 更小的通常更好 应该尽量使用可以正确存储数据的最小数据类型&#xff0c;更小的数据类型通常更快&#xff0c;因为它们占用更少的磁盘、内存和CPU缓存&#xff0c;并且处理时需要的CPU周期更少&#xff0c;但是要确保没有低估需要存储的…

项目管理:产品新人常犯的四个错误

作为产品经理&#xff0c;在刚进入职场的时候总会遇到各种各样的困难&#xff0c;产品小白常犯的错误&#xff1a; 1、拿到需求就开始写方案 很多新人在接到一个需求时&#xff0c;常常犯的错误就是直接开始写产品需求文档。很多时候我们的需求来自于老板或者甲方&#xff0c;…

LeetCode-350. 两个数组的交集 II

目录方法一&#xff1a;哈希表方法二&#xff1a;排序 双指针题目来源 350. 两个数组的交集 II 方法一&#xff1a;哈希表 由于同一个数字在两个数组中都可能出现多次&#xff0c;因此需要用哈希表存储每个数字出现的次数。对于一个数字&#xff0c;其在交集中出现的次数等于…

看板:自我管理的高效工具!

当在团队中在做看板管理的时候&#xff0c;我们一直强调的是看板中的工作项需要承载价值。通过拉动承载价值的工作项在看板中的流动&#xff0c;我们可以清晰地看到产品价值从需求端流动到交付端的进展及状态。也正因如此&#xff0c;我们需要及时识别看板中没有价值的工作项并…

解锁极狐GitLab 自动化功能:Webhook 触发 Pipeline 与计划流水线

本文来自&#xff1a; 杨洪丽 极狐GitLab 高级网站可靠性工程师(SRE) 只使用极狐GitLab 存放代码? 那你就 OUT 啦&#xff01; 如果你也遇到了如图 A / B / C / D 各位同学的烦恼&#xff0c;不要错过这篇能够让你日常工作更高效的内容。 使用极狐GitLab Webhook 触发 Pipelin…

JSON格式店铺商品列表api

对于做竟店分析来说&#xff0c;首先要知道店铺有哪些商品&#xff0c;店铺宝贝列表则提供了这样的入口&#xff0c;可以查看该店铺的所有宝贝&#xff0c;也可以通过关键词全局搜索来获取店铺宝贝&#xff0c;但搜索会夹带这很多推荐的结果在里面&#xff0c;会比较混乱。 淘宝…

Python语言零基础入门教程(二)

Python 变量类型 变量是存储在内存中的值&#xff0c;这就意味着在创建变量时会在内存中开辟一个空间。 基于变量的数据类型&#xff0c;解释器会分配指定内存&#xff0c;并决定什么数据可以被存储在内存中。 因此&#xff0c;变量可以指定不同的数据类型&#xff0c;这些变…

2023年不良资产项目尽调研究报告

第一章 不良资产的尽调方法 1.1 档案查阅 阅档是尽职调查工作的第一步也是最基础的环节。阅档过程是对资产进行了解的过程&#xff0c;阅档需要判断债权是否具有瑕疵&#xff0c;担保是否继续有效&#xff0c;初步了解各类资产所含的法律关系、状态、权益等。 通过查阅项目卷…

AI人工智能方向

AI人工智能方向 图像CV 图像处理&#xff0c;犬类识别&#xff0c;场景文字识别&#xff0c;人脸算法&#xff0c;图像增强&#xff0c;计算机影像&#xff0c;车型识别&#xff0c;物体检测&#xff0c;黄色图片识别&#xff0c;目标检测API文档Logo检测使用手册&#xff0c…

【数字孪生百科】每周认识一个数字孪生要素 —— 气泡图(Bubble Chart)

简介气泡图&#xff08;Bubble Chart&#xff09;是一种多变量的图片&#xff0c;是散点图的一种变体&#xff0c;气泡图其实是在散点图的基础上进行升级改造的&#xff0c;在原有的以横纵坐标为变量的基础上&#xff0c;引入第三个变量&#xff0c;用气泡的大小来表示&#xf…

Lesson 5. 分类模型决策边界与模型评估指标(上)

文章目录一、决策边界&#xff08;Decision Boundary&#xff09;基本概念与用途1. 决策边界基本概念与绘制方法2. 通过决策边界观察模型性能3. 逻辑回归决策边界与模型可解释性二、逻辑回归决策边界绘制与使用方法1. 定义决策边界绘制函数2. 决策边界绘制3. 逻辑回归决策边界与…

3、Maven——Maven创建java web工程,IDEA更改XML版本、Maven Search插件的安装与使用

目录 一、Maven创建java web工程 二、Maven创建java web项目的结构 1、添加项目目录 2、web.xml配置处理报错 三、IDEA更改XML版本 四、Maven Search插件的安装与使用 1、Maven Search的安装 2、Maven Search的使用 一、Maven创建java web工程 创建Empty Project空工程…

【U8+】用友U8试算UFO报表后,显示#公式错误;并且函数向导中没有用友账务函数。

【问题描述】 使用用友U816.1软件的时候&#xff0c; 打开UFO报表&#xff0c;计算资产负债表后&#xff0c;显示【#公式错误】。 【排查过程】 开始以为是选择的报表模板文件有问题&#xff0c;将其文件发送到其他U8客户端电脑上&#xff0c;操作正常。 那么可以认为报表模板…