二维数组

深入理解二维数组

  • 首先定义一个二维数组

    int a[2][3]={{1,2,3},{4,5,6}};
    #or int a[2][2]={1,2,3,4};
    

    新的理解:我们可以这样认为,a可以看作是一个一维数组,包含2个元素,每个元素恰好是包含3个整型的元素,a这个名字的值成了指向一个包含3个整型元素的数组的指针(你学过数组指针就该知道,他们是一个道理,后面我会讲解数组指针)

  • 然后开始讨论二维数组和数组名的关系

    a表示的是整个数组的首地址,a[0]表示的是第一行的首地址,这两者在数值上是一样的,但含义不同(或者说类型不同),数组名a是对于整个数组,a[0]是对于第一行。

#打印 a a[0] 的值和 a[0][0] 的地址
cout << a << endl;#0x7ffffffee120
cout << a[0] << endl;#0x7ffffffee120
cout << &a[0][0] << endl;#0x7ffffffee120

​ 所以a、a[0]的值和 a[0][0]的地址三个表达指向一个地址。

  • 接着看每一行

    cout << a[0] << endl;#0x7ffffffee120
    cout << a[1] << endl;#0x7ffffffee12c
    

    输出的结果是每行的首地址,两个地址之间相差12个字节,也就是三个int的大小。

  • 下面我们通过a[0]&a[0][0]这两个地址推导出其他位置的元素。

    # 由a[0]推出其他,这里以第1行第1列为例
    cout << a[0+1]+1 << endl;         #0x7ffffffee130
    cout << *(a[0+1]+1) << endl;      #5
    cout << a[0]+4 << endl;                #0x7ffffffee130
    cout << *(a[0]+4) << endl;         #5# 由&a[0][0]推出其他, 这里以第1行第1列为例
    cout << &a[0][0]+4<< endl;     #0x7ffffffee130
    cout << *(&a[0][0]+4) << endl; #5
    

    前两行通过加a[0]的索引得到其他行的首地址,然后再加偏移量得到元素地址,解引用获得该地址的值。

    之后两行是直接计算a[0]的偏移量得到元素地址,解引用获得该元素地址的值。最后两行与直接加偏移量的情况相同。

  • 由数组名得到其他元素

    a
    

    a是数组的名字,它的类型是“指向包含三个整型元素数组的指针”,默认指向第一行。

    a+1
    

    现在它指向了第二行,可以理解为当一个int*的指针指向一个数组的某一个int时,++他便指向了后一个int,同理,这里的数组指针指向了一个数组,所以++他就要指向下一个数组。

    *(a+1)
    

    若是int*解引用后我们拿到了int类型数据,那数组指针呢?实际上我们对一个数组的指针解引用,拿到的是一个数组

    *(a+1) == a[1]
    

    *(a+1)就是第二行的数组名,如果你觉得这样表达很奇怪,那么a[1]和他意义相同。

    我们现在拿到这个二维数组的第二行,第二行实际上就是一个一维数组,所以或许我们所有的处理都可以使用一维数组的方式。

    *(a+1)+ 1
    

    让他指向了这个数组中的第2个元素。

    *(*(a+1)+ 1)
    

    最后一步就可以拿到想要的值。

    我们来看看一些关于取地址的问题

    printf("%d\n",sizeof(a+1));//8//第二行的地址(指针的大小)
    printf("%d\n",sizeof(&a[0]+1));//8
    printf("%d\n",sizeof(*(&a[0]+1)));//12
    

    1、a是一个数组指针,加一后指向下一行他还是指针,sizeof中就是指针的大小了。

    2、给a[0]取地址?a[0]是一个一维数组的名字,也就是一个指针,再次取地址后的结果就是一个二级指针。而之前我们对数组指针解引用得到数组名,这次刚好相反,所以&arr[0]你可以理解为,我们现在拿到了一个数组指针,对数组指针+1就是让他指向第二行,sizeof中就是指针的大小。(所以数组指针也叫二级指针)

    3、对数组指针解引用拿到了第二行数组的名字,sizeof他就是第二行大小。

  • 最后进行个小测试

    int a[2][3] = {0};
    printf("%d\n",sizeof(a));//24//输出这个二维数组的大小
    printf("%d\n",sizeof(a[0][0]));//4//第一个元素的大小(int)
    printf("%d\n",sizeof(a[0]));//12//第一行的数组的大小
    printf("%d\n",sizeof(a[0]+1));//8//第一行第二个元素的地址(指针的大小)
    printf("%d\n",sizeof(*(a[0]+1)));//4//第一行第二个元素的大小
    printf("%d\n",sizeof(a+1));//8//第二行的地址(指针的大小)
    printf("%d\n",sizeof(*(a+1)));//12//第二行的大小
    printf("%d\n",sizeof(&a[0]+1));//8//第二行的地址(指针的大小)
    printf("%d\n",sizeof(*(&a[0]+1)));//12//第二行的大小
    printf("%d\n",sizeof(*a));//12//第一行的大小
    printf("%d\n",sizeof(a[2]));//12//虽然越界了但是sizeof只是一个关键字他不关心这块空间是否真的存在
    

    数组指针

为了更好的理解指针和二维数组的关系,我们先来定义一个指向 a 的指针变量 p:int (*p)[3] = a;

括号中的*表明 p 是一个指针,它指向一个数组,数组的类型为int [3],这正是 a 所包含的每个一维数组的类型。[ ]的优先级高于*,( )是必须要加的,如果赤裸裸地写作int *p[4],那么应该理解为int *(p[4]),p 就成了一个指针数组,而不是二维数组指针。

对指针进行加法(减法)运算时,它前进(后退)的步长与它指向的数据类型有关,p 指向的数据类型是int [3],那么p+1就前进 3×4 = 12 个字节,p-1就后退 12 个字节,这正好是数组 a 所包含的每个一维数组的长度。也就是说,p+1会使得指针指向二维数组的下一行,p-1会使得指针指向数组的上一行。

指针数组和二维数组指针有着本质上的区别:指针数组是一个数组,只是每个元素保存的都是指针。二维数组指针是一个指针,它指向一个二维数组,以上面的 a为例,它占用 8个字节的内存。

等价关系:

a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)

  • 小练习

    int main()
    {int arr[][3] = { 1, 2, 3, 4, 5, 6 };int(*p)[3];p = arr;cout << p[0][0] << " " << *(p[0] + 1) << " " << (*p)[2] << endl;return 0;
    }
    

    1、p[0][0]按照下标访问第一行第一个元素,输出1。
    2、*(p[0] + 1)这个让第一行指针指向第二个元素然后解引用,输出2。
    3、(*p)[2]等同于 *(*p+2),先解引用数组指针拿到第一行数组名,然后加到第3个位置解引用,输出3。

二维数组的传参

  • 使用二维数组的传统定义方法传递参数

    如果把数组名作为函数的参数的话,在编译的时候这个数组参数会自动退化为指针,因此以下两种写法虽然不同,但在编译之后是一样的,数组会退化成数组指针。

#include <iostream>
using namespace std;
/*以下两种写法本质上是一样的*/
int func1(int (*arr)[4]) {arr[0][2] = 20;return 0;
}
int func2(int arr[][4]) {arr[0][2] = 30;return 0;
}
int main(){int array[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; func2(array);cout << array[0][2] << endl;//fun1: 20 or fun2: 30return 0;
}
  • 使用动态分配内存的方式申请空间,可以使用**传递参数

    使用哪种方法定义的就使用哪种方式传参。

#include <iostream>
using namespace std;
int func1(int **arr) {cout << arr[0][1]<< endl;arr[0][2] = 20;    return 0;
}
int main(){int rows=3 ;int cols=4 ;int **arr = new int*[rows];//先使一个二级指针指向数组指针的地址for(int i = 0 ; i < rows ;++i ){arr[i] = new int [cols]();//为一级指针分配地址}arr[0][1]=100;func1(arr);cout << arr[0][2]<< endl;;//释放空间for(int i = 0; i < rows ;++i){delete [] arr[i];//先释放二维数组中每个元素指向的数组arr[i] = NULL;}delete [] arr;//在释放该数组指针;arr = NULL;
}

二维数组与数组指针详解相关推荐

  1. python二维图颜色函数_Python绘图之二维图与三维图详解

    各位工程师累了吗? 推荐一篇可以让你技术能力达到出神入化的网站"持久男" 1.二维绘图 a. 一维数据集 用 Numpy ndarray 作为数据传入 ply 1. import ...

  2. JAVAWEB实用技术——二维码的生成【详解】

    ​ 获取二维码对象:通过Qrcode()方法返回一个Qrcode类型的对象,利用该对象可调用各种设置属性对的方法来绘制具体的二维码: Qrcode textQrcode = new Qrcode(); ...

  3. python解析二维码_Python二维码生成识别实例详解

    前言 在 JavaWeb 开发中,一般使用 Zxing 来生成和识别二维码,但是,Zxing 的识别有点差强人意,不少相对模糊的二维码识别率很低.不过就最新版本的测试来说,识别率有了现显著提高. 对比 ...

  4. 二维码扫码登录详解【附简易实例代码(html+php+ios)】

    1.前言 我们在写一个不太了解的新功能的时候,又稳又快的一个方法就是借(chao)鉴(xi)其他的人的实现方法.所以我们先不急着开始写代码,先看一下各互联网巨头都是如何实现的. 首先来看一下淘宝的扫码 ...

  5. 一维二维水动力,水质模型详解

    数学模型在水环境评价.防洪评价和排污口论证等领域中的重要作用,随着人类活动的不断增加和环境问题的日益突出,对水资源和水环境的保护与管理变得至关重要.为了更好地理解和应对这些挑战,数学模型成为一种强大的 ...

  6. 【Java二维数组】(超详解)

    [Java二维数组](超详解) 什么是二维数组 二维数组的定义格式 访问二维数组 什么是二维数组 元素为一维数组的数组就称为二维数组 二维数组的定义格式 格式一:元素的数据类型[][] 数组的名字 = ...

  7. szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】

    树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...

  8. 指针数组与数组指针详解

    指针数组与数组指针详解 1.什么是指针数组和数组指针? 指针数组:指针数组可以说成是"指针的数组",首先这个变量是一个数组,其次,"指针"修饰这个数组,意思是说 ...

  9. php 数组 指针,php之数组指针详解

    本文主要和大家分享php之数组指针详解,首先我们会和大家分享php 数组指的针操作方法,希望能帮助到大家. 一.php 数组指针操作 利用php的内置函数:key,current,next(),pre ...

  10. linux c语言 malloc动态分配指针,C语言malloc函数为一维,二维,三维数组分配空间...

    c语言允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放,这些数据存储在堆区.可以根据需要,向系统申请 ...

最新文章

  1. 计算机网络PIC和SDV,SRBSDV和RBSDV检测技术的建立
  2. java环境变量设置与java查看安装路径
  3. Category 特性在 iOS 组件化中的应用与管控
  4. 项目助理这个工作怎么样_分析微信清理僵尸粉这个项目怎么样?
  5. Hive系列(一)metastore的认证和授权
  6. 【spingboot基础知识】相关问题汇总
  7. hexeditor 复制二进制值_MySQL并发复制演进
  8. leetcode938.RangeSumofBST
  9. 以眼睛的名义:一些光度学概念的解析
  10. 连通域的原理与Python实现
  11. 不写代码,实现动态网页设计-金蜘蛛网页设计器数据库设置
  12. 新手小白也看得懂的电脑win10安装教程
  13. docker使用docker compose file部署项目时,实现容器卷挂载,并对容器内文件夹赋予权限
  14. LTE网络有关系统消息(MIB/SIB)深度解析
  15. 中华成语库 v1.1 下载
  16. Twitter开发者账号【Twitter开发者文档系列3】——推特标准接口API的请求频率限制说明
  17. 关于OPC通信的认识
  18. dreamweaver html设计,如何用Dreamweaver设计网页
  19. 全国计算机系统登不上,电脑进不了系统如何解决?
  20. 【citavi使用】使用BibTex方式导入文献

热门文章

  1. 计算机应用 winxp,XP平台:计算机应用基础
  2. Diy Win7 OEMlogo
  3. 【自动升级后的错误】如何停止腾讯会议自动升级
  4. Android筑基——深入理解 LayoutInflater.inflate() 方法
  5. PKI体系及密码算法
  6. 2022年计算机软件水平考试网络管理员(初级)练习题及答案
  7. epson l201 l200清零软件 中文版 l111 l101清零软件 L350 L353 清零软件
  8. 分享一个开源的流程图绘制软件--Diagram Designer
  9. 激光投影仪对比激光电视 成像原理和适用范围
  10. wxid转扫一扫协议加好友