C++二维数组的定义及理解

一、前言

一维数组对于存储和处理一组数据很有用,但是有时候,很有必要使用多维数组。

例如,在统计学生成绩的程序中,教师可以将一个学生的所有科目考试分数记录在一个 double 类型的数组中。如果老师有40 名学生,那就意味着需要 40 个 double 类型数组来记录整个班级的分数。当然,在这里定义 40 个单独的一维数组显然是不合适的,而只要定义一个二维数组就可以了。

到目前为止,教程中所介绍的数组都只是一维数组,因为它们只能保存一组数据。二维数组,也称为 2D 数组,可以容纳多组数据。最好将二维数组看作具有行和列元素的表,一个二维数组可以被认为是一个带有 x 行和 y 列的表格。如下图 所示:
在这里插入图片描述
这张图是以二维数组显示了 3 行 4 列的考试成绩。
注意:数组的索引(下标)编号是从0开始的,所以3 行的编号为 0〜2, 4 列的编号则为 0〜3。数组中一共有 3*4=12 个元素。

二、二维数组的内存存储

一般的我们说的C/C++中的二维数组是定义在栈中的二维数组。比如定义了一个array[3][4],那就指的是定义了一个三行四列的矩阵形状的二维数组,如下图所示。这样的矩阵在内存中是以箭头右边的方式存放的,也就是说实际上我们定义的二维数组在内存中仍然像是一维数组那样连续存储的。可以想象为把一个矩阵一层层伸展铺平。
在这里插入图片描述

三、二维数组的定义

二维数组的定义可以是静态二维数组,可以是动态数组。

要定义一个二维数组,需要两个大小声明符。第一个是行数,第二个是列数。
定义方法有:
下面是一个 3 行 4 列的二维数组定义的例子:

方法一: 直接确定二维数组的行和列数

(1)确定二维数组的行和列数,但是不确定数组中的具体元素:

// 定义一个double类型的2行3列的二维数组,并没有确定数组中的具体元素
double score[3][4];

(2)不完全确定数组中的具体元素

// 定义一个int类型的2行3列的二维数组,并没有确定数组中的具体元素
int a[3][4] = {1,2,3,4,5,6}

(3)确定二维数组的行和列数,并且确定数组中的具体元素:

a.直接将二维数组铺平,写成一维数组的形式

 //a是一个2行3列的二维数组,并且确定好了里边的元素
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};  

b.使用标准的二维数组的初始化方式

int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

方法二: 不用指明二维数组的行数,但要指明列数

注意:二维数组必须写明列数,行数可以不写明!

/* 经典的写法
 */
int array[][4];
 
/* 一种更帅的写法,本质上是定义了一个数组指针
 */
int (*array)[4];

方法三: 动态分配的方式,这种写法比较灵活,在定义的时候不需要指明二维矩阵的行数和列数

/* 使用动态分配内存的形式,用malloc
 */
int **array // array[M][N]
array = (int **)malloc(M * sizeof(int *));
for(int i = 0; i < M; i++) {
    array[i] = (int *)malloc(N * sizeof(int));
}
 
// 释放
for(int i = 0; i < M; i++)
    free(array[i]);
free(array);
 
/* 使用动态分配内存的形式,用new
 */
int **array // array[M][N]
array = new int*[M];
for(int i = 0; i < M; i++) {
    array[i] = new int[N];
}
 
// 释放
for(int i = 0; i < M; i++) 
    delete [] array[i];
delete [] array;
 

不过按照这种形式实现的二维数组本质上已经不是我们之前说的传统的二维数组 了,采用此种方法定义的二维数组的结构如下图所示,这样的二维数组在堆上分配内存。也就是说这样实现的二维数组本质上已经是指针数组了。使用这样的方式分配二维数组空间之后,可以使用array[i][j]进行数组的读写操作没有问题,但是应该知道不同行的内存空间不一定连续。这是因为每一行的数组的内存都是使用malloc分配的,多次使用malloc分配的内存空间并不一定是连续的,这与在栈上分配的二维矩阵有着根本的不同,比如同样的对于顶底的二维数组array[3][4],不能再使用array[0][4]来访问array[1][0]了,这样会导致地址越界,就是因为地址不一定连续了。
在这里插入图片描述

参考博客: https://blog.csdn.net/qq_26822029/article/details/85037209

四、重点)二维数组行、列的理解(代码实践 )

double ary[][3] = { 0,1,2,3,4,5,6,7,8};

代码说明:以一维数组平铺展开二维数组的定义形式定义了一个二维数组,指明了3列,那么后边的具体元素会默认每三个为一行,共3行

以标准二维数组定义的方式,则定义为:很清晰的看到每三个元素为一个元素,用“{ }”扩着

double ary[][3] = { {0,1,2},{3,4,5} ,{6,7,8} }; 

那么这样定义的二维数组,我们在控制台输出看一下结果:

#include "pch.h"
#include <iostream>
using namespace std;
//二维数组问题
int main()
{
	//初始化二维数组ary,指明列数为3列
	double ary[][3] = { {0,1,2},{3,4,5} ,{6,7,8} };
	for (size_t i = 0; i < 3; i++) //输出3行
	{
		cout << "第" << i << "行"<<" ";
		for (size_t j = 0; j < 3; j++) //输出4列
		{
			cout << ary[i][j]<<"," ;
		}
		cout << endl;
	}
}

在这里插入图片描述
通过控制台输出的结果,我们确实看到输出3行3列正好是arr[ ][3]数组。

二维数组行、列溢出?

那么,小编的脑袋冒出了小小的疑惑:如果输出的行或者列超出了会产生什么结果?

a. 行溢出

在这里插入图片描述

我们看输出结果,可以发现如果是行溢出,那么多出的两行因为没有确定具体的元素,而产生随机行为,输出随机数。

原因:
这涉及到内存分配的问题,二维数组说白了还是一个指针,你声明的时候就相当于和系统说:“嘿,这块地是我的了,不要让别人抢去”
然后呢,你就可以自由访问了。
但是呢,别的地方内存还是在的,你硬要访问呢当然还是访问的到的,但是这个地方可能别的程序或者什么东西在用着,虽然有值(计算机总是二进制,总能读出一个数),但是如果你对它做一些修改什么的可能会有一定的安全隐患的。

这里需要说明的是,在不同的语言中,语法要求有所不同,出现行/列溢出可能不是产生随机行为,而是出现异常。

b.列溢出

在这里插入图片描述
如果列溢出可以发现,第一行多余的两列元素是第二行的元素,第二行的元素是第三行的元素,第三行多余两列元素是随机数,那么为什么是这样呢?
原因分析:
这是由于分配内存地址是连续内存的原因,数组长度和数组值是确定的:ary[][3]={0,1,2,3,4,5,6,7,8} 这个二维数组在内存地址中是连续的,所以当第一行列溢出后,会取第二行的存在的数组值,最后一行列溢出时,其实相当于上边的行溢出,直接溢出整个数组所在的内存空间了。

  • 72
    点赞
  • 275
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ariel_欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值