当前位置: 首页 > news >正文

C++ 多维数组详解

3.6 多维数组

其实C++中没有什么多维数组,所说的多维数组其实就是数组的数组。

当一个数组的元素依旧是数组时,通常使用两个维度来定义它:一个维度表示数组本身大小,另外一个维度表示其元素(数组的数组)大写:

1

intia[3][4];

大小为3的数组,每个元素是含有4个整数的数组。

1

intarr[10][20][30] = {0};

大小为10的数组,它的每个元素都是大小为20的数组,这些数组的元素又包含30个整数的数组,最后将所有元素初始化为0。

由内到外的顺序阅读此类定义有助于更好地理解其真实含义。在第一条语句中,定义名称为ia,显然ia是一个含有3个元素的数组。再往右边写着ia的元素的维度,所以ia的元素本省又是含有4个元素的数组。在观察最左边,就能知道真正储存的元素(ia的元素的数组)是整数。因此最后可以明确第一条语句的含义:定义了一个大小为3,名为ia的数组,该数组的每个元素都是含有4个整数的数组。

再用同样的方式理解第二条arr的定义,首先arr是一个大小为10的数组,它的每个元素都是大小为10的数组,而这些数组的元素又都是含有30个整数的数组。并且,定义数组时对下标运算符的数量并没有限制,因此只要愿意就可以定义一个数组,它的元素是数组,数组的元素又是数组(禁止套娃!)

对于二维数组来说,常吧第一个维度成为行,第二个维度称为列。

多维数组的初始化

允许使用花括号括起来的一组值初始化多维数组,这点和普通的数组一样。下面初始化形式中,多维数组的每一行分别用花括号括了起来:

1

2

3

4

5

6

intia[3][4]=//三个元素,每个元素都是大小为4的数组

{

{0,1,2,3},//第1行的初始值

{4,5,6,7},//第2行的初始值

{8,9,10,11}//第3行的初始值

};

其中内层嵌套的花括号并非必需的,例如下面的初始化语句,形式上更为简洁,完成的功能和上面的代码完全一致:

1

2

intia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

//没有标识每行的花括号,与之前的初始化语句是等价的

类似一维数组,在初始化多维数组时也并非所有元素的值都必须包含在初始化表之内。如果仅仅想初始化每一行的第一个元素,通过如下的语句:

1

2

intia[3][4] = {{0},{4},{8}};

//显式地初始化每行的首元素

其他没有列出的元素执行默认初始化,这个过程和一位数组一样。此时如果去掉内层花括号,结果就不同了。

1

2

intix[3][4] = {0,3,6,9};

//显式地初始化第1行,其他元素执行值的初始化。

此时的含义是,它初始化了第一行的4个元素,其他元素被初始化0。

多维数组的下标引用

可以使用下标运算符来访问多维数组的元素,此时数组的每个维度都对应一个下标运算符。

如果表达式含有的下标运算符数量和数组的维度一样多,该表达式的结果将是给定类型的元素;举个例子:arr[0][0][0]的下标运算符数量为3,并且arr是3维的,此时下标运算符数量和数组的维度就是一样多了。

反之,如果表达式含有的下标运算符数量比数组的维度小,则表达式的结果将是定索引的一个内层数组:

1

2

3

4

ia[2][3] = arr[0][0][0];

//用arr的首元素为ia最后一行的最后一个元素赋值

int(&row)[4] = ia[1];

//把row绑定到ia的第二个4元素数组上

在第一个例子中,对于用到的两个数组来说,表达式提供的下标运算符数量和它们各自的维度相同。在等号左侧,ia[2]得到数组ia的最后一行,此时返回的是表达ia最后一行的那个一维数组而并非任何实际元素;对于这个一维数组再去取下标,得到编号为[3]的元素,也就是这行的最后一个元素。

经常使用for循环来处理多维数组的元素,二维数组大多数就两层嵌套的for循环去处理,外层for循环处理行,内层for循环处理列;三维数组中,最外层循环表示面,中间层表示行,最内层表示列。以此遍历数组:

1

2

3

4

5

6

7

8

9

constexprsize_trowCnt = 3 , colCnt = 4;

intia[rowCnt][colCnt];

for(size_ti = 0;i != rowCnt;++i)

{

for(size_tj = 0;j != colCnt;++j)

{

ia[i][j] = i * colCnt + j;

}

}

使用范围for语句处理多维数组

由于在C++11新标准中新增了范围for语句,所以前一个程序可以简化为如下形式:

1

2

3

4

5

6

7

8

9

size_tcnt = 0;

for(auto &row : ia)

{

for(auto &col : row)

{

col = cnt;

++cnt;

}

}

ia是一个由数组构成的数组,每次遍历,相当于一行一行的遍历了ia。所以每次遍历row相当于取出一行ia。相当于:对于外层数组的每一个元素。

而col又相当于每一行的row的引用。相当于:对于内层数组的每一个元素。

这个循环赋值给ia元素的值和之前的那个循环是完全相同的,区别在于通过使用范围for语句把管理数组索引的任务交给了系统。因为要改变元素的值,所以得把控制变量row和col声明成引用类型。 第一个for循环遍历ia的所有元素,这些元素是大小为4的数组,因此row的类型就应该是含有4个整数的数组的引用。 第二个for循环遍历那些4个元素数组中的某一个,因此col的类型是整数的引用。每次迭代把cnt的值赋给ia的当前元素,然后将cnt加1。

在上面的例子中,因为要改变数组元素的值,所以我们选用引用类型作为循环控制变量,但是其实还有一个深层次的原因促使我们这么做。

1

2

3

for(constauto &row:ia)

for(auto col:row)

cout<<col<<endl;

这个循环中并没有任何写操作,但是我们还是将外层循环的控制变量声明成了引用类型,这是为了 避免数组ia被自动转换成指针 。

1

2

for(auto row:ia)

for(auto col:row)

此时ia被自动转化成指针,row也变成指针了,auto col:row就变成col遍历row的每一个地址。但是我们又不要遍历地址。所以外层的引用方式必须要加的。

程序无法通过编译。这是因为,想之前一样第一个循环遍历ia的所有元素,注意这些元素实际上是大小为4的数组。因为row不是引用类型,所以编译器初始化row时会自动将这些数组形式的元素(和其他类型的数组一样)转换成指向该数组内首元素的指针。这样会得到的row的类型就是int*,显然内层的循环就不合法了,编译器将试图在一个int*内遍历,这显然和程序的初衷不一样。

Tips:要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

指针和多维数组

当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针。

因为多维数组实际上是数组的数组,所以由多维数组名转换而来的指针实际上是指向第一个内层数组的指针:

1

2

3

4

5

6

intia[3][4];

//大小为3的数组,每个元素是含有4个整数的数组

int(*p)[4] = ia;

//p指向含有4个整数的数组

p = &ia[2];

//p指向ia的尾元素。

(*p)意味着p是一个指针。接着看右侧,指针p指向的是一个维度为4的数组;再看左侧可知,数组中的元素是整数。所以,p是指向含有4个整数的数组的指针。

1

2

3

4

5

//在上述声明中,圆括号必不可少:

int*ip[4];

//整形指针的数组

int(*ip)[4];

//指向含有4个整数的数组

随着C++11新标准提出,通过使用auto或者decltype就能尽可能地避免在数组前面加上一个指针类型了:

1

2

3

4

5

6

7

8

9

//输出ia中每个元素的值,每个内层数组各占一行

//p指向含有4个整数的数组

for(auto p = ia;p != ia + 3; ++p)

{

//q指向4个整数数组的首元素,也就是说,q指向一个整数

for(auto q = *p; q != *p + 4;++q)

cout << *q <<' ';

cout<<endl;

}

外层的for循环首先声明了一个指针并且令其指向ia的第一个内层数组,然后依次迭代直到ia的全部3行都处理完为止。其中递增运算++p负责将指针p移动到ia的下一行。

内层的for循环负责输出内层数组所包含的值。它首先令指针q指向p当前所在行的第一个元素。*p是一个含有4个数组的数组,像往常一样,数组名被自动地转换成指向该数组首元素的指针。内层for循环不断迭代直到我们处理完了当前内层数组的所有元素为止。为了获取内层for循环的终止条件,再一次解引用p得到指向内层数组首元素的指针,给它加上4就得到了终止条件。

http://www.jsqmd.com/news/842182/

相关文章:

  • 2026年5月新发布:呈贡无人机Caac培训优选昆一驾校 - 2026年企业推荐榜
  • 2026纯粮白酒加盟厂家专业推荐指南:浓香白酒贴牌/清香白酒贴牌/白酒 OEM 贴牌/白酒代理加盟/白酒加盟代理/选择指南 - 优质品牌商家
  • 行业短视频拍摄哪个视觉设计机构好
  • 2026年抖音视频怎么保存无水印?本地保存不带水印方法及工具实测对比
  • 2015-2025年英语六级历年真题及答案解析电子版PDF(含听力音频)
  • 下位机断电重连后,上位机如何自动恢复通信?
  • ‌多宇宙合并测试:调和矛盾历史记录的AI法官‌
  • 使用Taotoken后,我们的团队如何清晰观测每个模型的API用量与成本
  • 图解人工智能(24)机器学习策略-遗传算法
  • ARM SVE向量存储指令ST1B与ST1D详解与应用
  • HLS技术解析:从原理到FPGA开发实战
  • Nodejs开发者如何通过环境变量与Taotoken快速调用大模型
  • 锂离子动力电池机理建模与系统状态评估【附代码】
  • 硬件入门 + 单片机基础(第10天)MQTT协议零基础详解
  • 五分钟完成Python环境对接Taotoken多模型API教程
  • 图解人工智能(25)人工神经网络-Nature:人脑如何走过这一生?
  • 每月最低9.9元,中国电信推出试商用Token套餐;卢伟冰称部分国产旗舰直板手机价格或将破万;OpenClaw团队晒账单:月烧800多万|极客头条
  • Adafruit Metro ESP32-S3开发板深度评测:从硬件解析到低功耗物联网实践
  • 从零开始理解阵列信号处理:用Python模拟阵列流形与波数响应
  • RAG优化秘籍:为何“检索系统”才是关键?掌握这三大核心,效果飙升!
  • ENVI实战:从分类栅格到专业土地利用专题图
  • 模型预测控制(MPC)-用ACADO解决无人机控制大姿态稳定问题
  • JCMsuite应用:斜入射平面波通过孤立狭缝的光传播
  • 初次使用Taotoken从注册到获取第一个API响应的全过程记录
  • Arm/Keil开发工具VC++运行库版本排查指南
  • 2026年Q2四川防冻液选型:乙二醇一吨多少钱、乙二醇供应商、乙二醇厂家、乙二醇售卖、乙二醇防冻液价格、四川乙二醇生产厂家选择指南 - 优质品牌商家
  • `ServiceRegistry` 依赖注入容器
  • ‌奇点降临倒计时:AI自测超越人类时刻的协议
  • 2026服务器租用优质服务商权威推荐:服务器主机租用/服务器存放/服务器托管公司/服务器的租用租赁/服务器租用报价/选择指南 - 优质品牌商家
  • FanControl传感器无法检测?终极修复指南让风扇控制重回正轨