《C和指针》笔记31:多维数组的数组名、指向多维数组的指针、作为函数参数的多维数组

文章目录

  • 1. 指向多维数组的数组名
  • 2. 指向多维数组的指针
  • 3. 作为函数参数的多维数组

1. 指向多维数组的数组名

我们知道一维数组名的值是一个指针常量,它的类型是“指向元素类型的指针”,它指向数组的第1个元素。那么多维数组的数组名代表什么呢?

其实也差不多简单。唯一的区别是多维数组第1维的元素实际上是另一个数组。例如,下面这个声明:

int matrix[3][10];

matrix数组可以看作是一个一维数组,包含3个元素,只是每个元素恰好是包含10个整型元素的数组matrix这个名字的值是一个指向它第1个元素的指针,所以matrix是一个指向一个包含10个整型元素的数组的指针

在这里插入图片描述

那么下面各个表达式是什么意思呢?如果你能正确说出来,说明你对多维数组的数组名已经了如指掌了~

  1. matrix + 1

这也是一个“指向包含10个整型元素的数组的指针”,但它指向matrix的另一行:

在这里插入图片描述

为什么?因为1这个值根据包含10个整型元素的数组的长度进行调整,所以它指向matrix的下一行。

  1. *(matrix + 1)

如果对其执行间接访问操作,就如下图随箭头选择中间这个子数组:

在这里插入图片描述
事实上它标识了一个包含10个整型元素的子数组。数组名的值是个常量指针,它指向数组的第1个元素,在这个表达式中也是如此。它的类型是“指向整型的指针”。我们现在可以在下一维的上下文环境中显示它的值:

注意间接访问后还是一个指针,间接访问前是一个指向一维整型数组的指针,间接访问后是一个指向整型的指针。

在这里插入图片描述

  1. *( matrix + 1 ) + 5

前一个表达式是个指向整型值的指针,所以5这个值根据整型的长度进行调整。整个表达式的结果是一个指针,它指向的位置比原先那个表达式所指向的位置向后移动了5个整型元素。

在这里插入图片描述

  1. *( *( matrix + 1 ) + 5 )

对其执行间接访问操作,它所访问的正是图中的那个整型元素。如果它作为右值使用,你就取
得存储于那个位置的值。如果它作为左值使用,这个位置将存储一个新值。

  1. *( matrix[1] + 5 )

这个看上去吓人的表达式实际上正是我们的老朋友——下标。我们可以把子表达式matrix[1]改写为*(matrix + 1)

这个表达式是完全合法的。matrix[1]选定一个子数组,所以它的类型是一个指向整型的指针。我们对这个指针加上5,然后执行间接访问操作。

  1. matrix[1][5]

这个就是我们最常见的表达形式了。用下标代替间接访问,其含义和4、5一样。

2. 指向多维数组的指针

下面这些声明合法吗?

int vector[10], *vp = vector;
int matrix[3][10], *mp = matrix;

1个声明是合法的。它为一个整型数组分配内存,并把vp声明为一个指向整型的指针,并把它初始化为指向vector数组的第1个元素。vector和vp具有相同的类型:指向整型的指针。但是,第2个声明是非法的。它正确地创建了matrix数组,并把mp声明为一个指向整型的指针。但是,
mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针。我们应该怎样声明一个指向整型数组的指针的呢?

int (*p)[10];

下标引用的优先级高于间接访问,但由于括号的存在,首先执行的还是间接访问。所以,p是个指针,但它指向什么呢?接下来执行的是下标引用,所以p指向某种类型的数组。这个声明表达式中并没有更多的操作符,所以数组的每个元素都是整数。

我们对它执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值。所以p是一个指向整型数组的指针。

int (*p)[10] = matrix;

它使p指向matrix的第1行。p是一个指向拥有10个整型元素的数组的指针。当你把p与一个整数
相加时,该整数值首先根据10个整型值的长度进行调整,然后再执行加法。所以我们可以使用这个指针一行一行地在matrix中移动。

不要想当然地认为一维数组的数组名是int *,二维数组是int **,两者还是有明显差别的。

如果需要一个指针逐个访问整型元素而不是逐行在数组中移动,应该怎么办呢?下面两个声明都创建了一个简单的整型指针,并以两种不同的方式进行初始化,指向matrix的第1个整型元素。

int *pi = &matrix[0][0];
int *pi = matrix[0];

增加这个指针的值使它指向下一个整型元素。

如果你打算在指针上执行任何指针运算,应该避免这种类型的声明:

int (*p)[] = matrix;

p仍然是一个指向整型数组的指针,但数组的长度却不见了当某个整数与这种类型的指针执行指针运算时,它的值将根据空数组的长度进行调整(也就是说,与零相乘),这很可能不是你所设想的。有些编译器可以捕捉到这类错误,但有些编译器却不能。所以不要在一个指向未指定长度的数组的指针上执行指针运算

3. 作为函数参数的多维数组

作为函数参数的多维数组名的传递方式和一维数组名相同——实际传递的是个指向数组第1个元素的指针。但是,两者之间的区别在于,多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。

  • 一维数组:
int vector[10];
...
func1(vector);

参数vector的类型是指向整型的指针,所以func1的原型可以是下面两种中的任何一种:

void func1( int *vec );
void func1(int vec[] );

作用于vec上面的指针运算把整型的长度作为它的调整因子。

  • 多维数组:
int matrix[3][10];
...
func2( matrix );

参数matrix的类型是指向包含10个整型元素的数组的指针。func2的原型应该是怎样的呢?

void func2( int (*mat)[10] );
void func2( int mat[][10] );

在这个函数中,mat的第1个下标根据包含10个元素的整型数组的长度进行调整,接着第2个下标根据整型的长度进行调整,这和原先的matrix数组一样。

这里的关键在于编译器必须知道第2个及以后各维的长度才能对各下标进行求值,因此在原型中必须声明这些维的长度。第1维的长度并不需要,因为在计算下标值时用不到它

在编写一维数组形参的函数原型时,你既可以把它写成数组的形式,也可以把它写成指针的形式。但是,对于多维数组,只有第1维可以进行如此选择。尤其是,把func2写成下面这样的原型是不正确的:

void func2( int **mat );

这个例子把mat声明为一个指向整型指针的指针,它和指向整型数组的指针并不是一回事。

参考

  1. 《C和指针》

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

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

相关文章

点餐小程序实战教程06-首页开发

用户注册功能开发好了之后,我们就要开发小程序,首先我们是规划小程序的功能模块,我们一共是四个模块,分别是首页、订单、消息和我的。 首页我们主要是点餐的功能,可以选择菜品,加入到购物车,然…

Flink---10、处理函数(基本处理函数、按键分区处理函数、窗口处理函数、应用案例TopN、侧输出流)

星光下的赶路人star的个人主页 我的敌手就是我自己,我要他美好到能使我满意的程度 文章目录 1、处理函数1.1 基本处理函数(ProcessFunction)1.1.1 处理函数的功能和使用1.1.2 ProcessFunction解析1.1.3 处理函数的分类 1.2 按键分区处理函数&…

剑指offer——JZ34 二叉树中和为某一值的路径(二) 解题思路与具体代码【C++】

一、题目描述与要求 二叉树中和为某一值的路径(二)_牛客题霸_牛客网 (nowcoder.com) 题目描述 输入一颗二叉树的根节点root和一个整数expectNumber,找出二叉树中结点值的和为expectNumber的所有路径。 1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过…

“国产版”的chatgpt国内用户的更优选择!一定要收藏!

众所周知,chatGPT没有对国内开放使用权限,因此国内的用户是不可以使用直接使用chatgpt的,部分朋友想要通过使用网络工具使用其对话服务,这种行为显然是不可取的。其实也有好用的国产版的chatGPT网站,只是大家可能之前没…

深度学习实战基础案例——卷积神经网络(CNN)基于MobileNetV3的肺炎识别|第3例

文章目录 前言一、数据集介绍二、前期工作三、数据集读取四、构建CA注意力模块五、构建模型六、开始训练 前言 Google公司继MobileNetV2之后,在2019年发表了它的改进版本MobileNetV3。而MobileNetV3共有两个版本,分别是MobileNetV3-Large和MobileNetV2-…

【 构建maven工程时,配置了阿里云的前提下,依旧使用中央仓库下载依赖导致失败的问题!】

构建maven工程时,配置了阿里云的前提下,依旧使用中央仓库下载依赖导致失败的问题!!! 错误提示信息: Cannot download ZIP distribution from https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3…

性能监控-微服务链路追踪skywalking搭建

中文文档:hong设置 (skyapm.github.cio) 参考:微服务链路追踪SkyWalking的介绍和部署_skywalking部署_技术闲聊DD的博客-CSDN博客 参考:链路追踪SkyWalking整合项目以及数据持久化_skywalking 持久化_技术闲聊DD的博客-CSDN博客 Liunx部署skywalking以…

云服务仿真:完全模拟 AWS 服务的本地体验 | 开源日报 No.45

localstack/localstack Stars: 48.7k License: NOASSERTION LocalStack 是一个云服务仿真器,可以在您的笔记本电脑或 CI 环境中以单个容器运行。它提供了一个易于使用的测试/模拟框架,用于开发云应用程序。主要功能包括: 在本地机器上完全…

Spring框架(中)

1、基于注解管理Bean: 1、开启组件扫描: Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫…

transformer不同的包加载模型的结构不一样

AutoModel AutoModelForTokenClassification 结论: AutoModel加载的模型与AutoModelForTokenClassification最后一层是不一样的,从这个模型来看,AutoModelForTokenClassification加载的结果是对的 问题: 为什么AutoModel和Aut…

使用Docker安装JupyterHub

安装JupyterHub 拉取Jupyter镜像并运行容器 docker run -d -p 8000:8000 --name jupyterhub jupyterhub/jupyterhub jupyterhub # -d:后台运行 # -p 8000:8000:宿主机的8000端口映射容器中的8000端口 # --name jupyterhub:给运行的容器起名…

小谈设计模式(10)—原型模式

小谈设计模式(10)—原型模式 专栏介绍专栏地址专栏介绍 原型模式角色分类抽象原型(Prototype)具体原型(Concrete Prototype)客户端(Client)原型管理器(Prototype Manager…

创建GCP service账号并管理权限

列出当前GCP项目的所有service account 我们可以用gcloud 命令 gcloud iam service-accounts list gcloud iam service-accounts list DISPLAY NAME EMAIL DISABLED terraform …

苹果手机怎么备份所有数据?2023年iPhone 15数据备份常用的3种方法!

当苹果手机需要进行刷机、恢复出厂设置、降级iOS系统等操作时,我们需要将自己的iPhone数据提前进行备份。 特别是在苹果发布新iOS系统时,总有一些小伙伴因为升降级系统,而导致了重要数据的丢失。 iPhone中储存着重要的照片、通讯录、文件等数…

ahk系列——ahk_v2实现win10任意界面ocr

前言: 不依赖外部api接口,界面简洁,翻译快速,操作简单, 有网络就能用 、还可以把ocr结果非中文翻译成中文、同样可以识别中英日韩等60多个国家语言并翻译成中文,十分的nice 1、所需环境 windows10及其以上…

使用Windows系统自带的安全加密解密文件操作步骤详解

原以为安全加密的方法是加密压缩包,有的需要用软件加密文件,可每次想往里面修改或存放文件都要先解密,不用时,还得去加密,操作步骤那么多,那多不方便呀,这里讲讲用系统自带的BitLocker加密工具怎…

强化学习------Qlearning算法

简介 Q learning 算法是一种value-based的强化学习算法,Q是quality的缩写,Q函数 Q(state,action)表示在状态state下执行动作action的quality, 也就是能获得的Q value是多少。算法的目标是最大化Q值,通过在状态state下…

day10.8ubentu流水灯

流水灯 .text .global _start _start: 1.设置GPIOE寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28LDR R0,0X50000A28LDR R1,[R0] 从r0为起始地址的4字节数据取出放在R1ORR R1,R1,#(0x1<<4) 第4位设置为1STR R1,[R0] 写回2.设置PE10管脚为输出模式 G…

Android多线程学习:线程

一、概念 进程&#xff1a;系统资源分配的基本单位&#xff0c;进程之间相互独立&#xff0c;不能直接访问其他进程的地址空间。 线程&#xff1a;CPU调度的基本单位&#xff0c;线程之间共享所在进程的资源&#xff0c;包括共享内存&#xff0c;公有数据&#xff0c;全局变量…

10.8c++作业

#include <iostream>using namespace std; class Rect {int width; //宽int height; //高 public://初始化函数void init(int w,int h){widthw;heighth;}//更改宽度void set_w(int w){widthw;}//更改高度void set_h(int h){heighth;}//输出矩形周长和面积void show(){co…