C语言进阶指南(12)(数组指针与二维数组)

欢迎来到博主的专栏——C语言进阶指南
博主的id:reverie_ly

数组指针

数组指针的标准声明如下

type (*parr)[num]

type是指针指向的数组的类型,[num]是指针指向的数组的元素大小。
顾名思义,数组指针就是用来指向数组的指针变量

int arr[5];
int*p=arr;
int*parr)[5]=arr;

与一级指针不同(int*),这个指针变量的类型是(int*[5]).
虽然parr与p的赋值都是arr,但是这个arr在不同的变量上的类型是不一样的。

p中的arr是发生了类型降级的,也就是说arr从int[5]的类型变成了int*
而parr中的arr是int*[5]的,发生了类型转换,使arr的类型从int[5],变成了int*[5].

从下面的示例中也能看出区别

#include<stdio.h>
int main()
{int arr[5] = { 1,2,3,4,5 };int* p = arr;int(*parr)[5] = arr;for (int i = 0; i < 5; i++){printf("%d ", (*parr)[i]);//parr先被解引用,再取下标访问}for (int i = 0; i < 5; i++){printf("%d ", p[i]);//p直接去下标访问}return 0;
}

在该例中,parr先被解引用成int[5]类型的变量(即数组arr的类型),再去下标进行访问。

也就是说(*parr)是等价于(arr)的。

而p是利用了指针算术运算的原理进行访问的,在前面的文章中提到过

*(p+i)=p[i]

注意区分这两种实现方法的数据类型在解引用时的区别,parr在使用前被解引用成int[5]类型的arr的地址。再解引用成int类型的arr的元素。

而p则是从(int*)类型解引用成int类型的arr的元素。

数组指针与指针数组

数组指针与指针数组的声明非常类似。这里将两种声明进行对比
数组指针:

type (*parr)[num]

指针数组:

type *parr[num]

可以发现指针数组与数组指针的区别仅仅在于标识符与声明结合优先度不同

在没有括号的情况下,标识符优先与[]结合成为一个数组的声明符
用括号()将标识符与*优先结合,使得标识符与*组成一个指针的声明符

虽然指针数组与数组指针的声明非常相似,但是性质是完全不同的。

数组指针是一个指针,它是一个指向指定类型数组的指针,比如int(*parr)[5],这个数组指针指向的是int[5]类型的数组。(一级指针也能指向数组,但是一级指针指向的只是数组的首元素(int*)的地址,而数组指针指向的是整个数组[int[num]],这一特点数组指针将在二维数组的使用中展现出来)

在这里插入图片描述

而指针数组是一个数组,数组中的元素数据类型都是指针类型

在这里插入图片描述

数组指针与二维数组

我们可以发现在上面的例子中数组指针是能被指针替代的,那么数组指针的意义何在?
回顾前面的文章可以了解到,指针进行加减N的算术运算时,会跳过(或往前)N个等数据类型大小的字节。比如int*类型的指针加1时会跳过int类型大小(4字节)的字节。那么数组指针进行算术加减时跳过几个字节呢?

int arr[5];
int(*parr)[5]=arr;
printf("%p\n%p",parr.parr+1);

运行发现parr+1比parr高20个字节的地址。这是由于int*[5]对应的数据类型是int5。而int[5]类型对应的字节就是20字节。
对应到二维数组中,如果程序构造一个数组指针指向二维数组

	int arr[4][5] = {0};int(*parr)[5] = arr;

那么二维数组arr每一行都有五个int类型的元素(20字节),而parr+1跳过20字节就是跳过一行。
在这里插入图片描述
parr+1对应的地址是&arr[1],那么解引用parr+1则等价于arr[1].

打开vs中的监视窗口可以发现,arr[1]是int[5]类型的数据(arr是int[4][5]).于是将(int*[5])类型的parr+1解引用,parr的类型也变成int[5],而且parr+1的地址和arr[1]的地址是一样的!!!

在这里插入图片描述
那么也就是说 *(parr+1)等价于arr[1],那么*(parr+1)[2]不就等价于arr[1][2]么。 *正是如此,我们就可用通过数组指针来操作二维数组了

int main()
{int arr[4][5] = {0};int(*parr)[5] = arr;for (int i = 0; i < 4; i++){for (int j = 0; j < 5; j++){(*(parr + i))[j] = j;//要注意[]的优先级比*高,所以要先将parr+i解引用。}}return 0;
}

不过上例中,数组指针的作用还没有完全体现出来,既然二维数组就能实现的程序,为什么还要数组指针来作为中间商?

事实上数组指针的真正作用在于,数组指针作为函数原型的参数时。此时函数的参数可以传递二维数组的数组名作为函数的实际参数。

void print(int(*parr)[5], int row-1, int col-1)
{printf("%d",(*(parr + row))[col]);
}

这个函数的意义则是打印出某行某列的数。如果让上上个例子调用这个函数,那么函数的调用语句可以为
print(arr,2,4);//打印第2行第四列的元素(arr[1][3]).

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

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

相关文章

Python的换行和转义:深入理解代码排版与字符串处理

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是涛哥&#xff0c;今天为大家分享 Python的换行和转义&#xff1a;深入理解代码排版与字符串处理&#xff0c;全文2700字&#xff0c;阅读大约8分钟。 在Python编程中&#xff0c;正确使用换行和转义字符是保…

MySQL- CRUD

一、INSERT 添加 公式 INSERT INTO table_name [(column [, column...])] VALUES (value [, value...]); 示例&#xff1a; CREATE TABLE goods (id INT ,good_name VARCHAR(10),price DOUBLE ); #添加数据 INSERT INTO goods (id,good_name,price ) VALUES (20,华为手机,…

【带头学C++】----- 八、C++面向对象编程 ---- 8.5 struct结构体类型增强使用说明

目录 8.5 struct结构体类型增强使用说明 8.5.1 C结构体可以定义成员函数 8.5.2 c中定义结构体变量可以不加struct关键字 8.6 bool布尔类型关键字 8.5 struct结构体类型增强使用说明 第六章对结构体的使用、内存对齐以及数组、深拷贝和浅拷贝进行了一个详细的说明&#xff0c…

Redis队列stream,Redis多线程详解

Redis 目前最新版本为 Redis-6.2.6 &#xff0c;会以 CentOS7 下 Redis-6.2.4 版本进行讲解。 下载地址&#xff1a; https://redis.io/download 安装运行 Redis 很简单&#xff0c;在 Linux 下执行上面的 4 条命令即可 &#xff0c;同时前面的 课程已经有完整的视…

方差分析汇总

一文整理了方差分析的全部内容&#xff0c;包括方差分析的定义&#xff08;基本思想、检验统计量的计算、前提条件&#xff09;、方差分析分类&#xff08;单因素、双因素、多因素、事后多重比较、协方差分析、重复测量方差分析&#xff09;、方差分析流程&#xff08;数据格式…

HTTP/3 为什么正迅速崛起

超文本传输协议&#xff08;HTTP&#xff09;作为互联网的基石&#xff0c;一直在网页加载、视频流传输、应用获取数据等方方面面发挥重要作用。 去年&#xff0c;负责定义互联网技术的互联网工程任务组&#xff08;IETF&#xff09;将该协议的最新版本 HTTP/3 定为标准。在此…

大模型训练为什么用A100不用4090

这是一个好问题。先说结论&#xff0c;大模型的训练用 4090 是不行的&#xff0c;但推理&#xff08;inference/serving&#xff09;用 4090 不仅可行&#xff0c;在性价比上还能比 H100 稍高。4090 如果极致优化&#xff0c;性价比甚至可以达到 H100 的 2 倍。 事实上&#x…

如何控制Spring工厂创建对象的次数?详解Spring对象的声明周期!

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

SPSS生存分析:寿命表分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

15.spring源码解析-invokeBeanFactoryPostProcessors

BeanFactoryPostProcessor接口允许我们在bean正是初始化之前改变其值。此接口只有一个方法: void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);有两种方式可以向Spring添加此对象: 通过代码的方式: context.addBeanFactoryPostProcessor 通过xml…

天鹅湖国家旅游度假区 | 展柜OLED透明屏:创新展示提升互动体验

天鹅湖国家旅游度假区 | 展柜OLED透明屏 产品&#xff1a;一块55寸OLED透明屏嵌入玻璃安装 应用场景&#xff1a;用在天鹅湖国家旅游度假区——三门峡城市文化客厅展馆中的一个透明展示柜&#xff0c;用一块55寸OLED透明屏嵌入展示柜的玻璃&#xff0c;让观众即可以看到展柜里…

【RTP】5:从network收到rtp包到组帧之间的数据传递

m79 代码。从网络中收到rtp、rtcp 后交给call 进行处理这是因为call 具有PacketReceiver 的能力。收到的包是一个 :CopyOnWriteBuffer 类型:rtc::CopyOnWriteBuffer packetclass Call PacketReceiver 准备delivery包:返回delivery结果:}成功、包错误、ssrc未知 D:\zhb-dev\…