C++ 二维数组,二维指针,数组指针深入研究(二维指针调用二维数组的方法)

关于用指针方式访问二维数组的问题

发现二维指针和二维数组很容易被混淆。这里详细梳理一遍
首先说明几种类型:


int a[2][4] = { 1,2,3,4,5,6,7,8 };//二维数组
	int **p;	//二重指针,跨度为sizeof(int *)
	int(*q)[4]; /定义一个指向数组的指针,指向含4个int元素的一维数组。跨度为4*sizeof(int);
	int * q2[4];	//定义了一个数组 里面存有int * 类型的指针 跨度为sizeof(int *)

关于跨度,他是指针偏移一个单位,其对应的物理地址的偏移量。举个例子:

int *p =0 //p 指向地址0
p2=p+1 //p2指向地址4

这里p的跨度为4,因为p增加一位,对应地址增加4位(int 在计算机中占4个字节的存储量)
每个指针,在机器中他们就是一个数,他的大小都是固定的,但是其不同的类型和跨度完全是编译器阶段赋予他们的含义,比如:

int a=3
int *p1=&a;
 float *p2=&a;

他们在存贮在计算机中只是两个数,但是你定义的时候一个使用int,一个使用float,所以编译器认定p1跨度为4,p2跨度为8

二维数组的在计算机中依然是以一维线性关系排布的
以二维数组a[2][3]为例,a是由数组a[0]、a[1]组成的一维数组,因此,数组名a可以看作是数组 { a[0], a[1] } 的首地址,即a==&a[0]。然后,对于二维数组,在这里引入两个概念:
1、一级指针:带一个下标的数组名是一级指针,偏移量是一个元素的长度,它所指对象是数组元素。
2、二级指针(二重指针):不带下标的数组名是一个逻辑上的二级指针,所指对象是行向量,偏移量是一行元素的存储长度。
定义一个二维数组和二级指针变量

int a[2][3],**p;

这里要是想使用p访问二维数组a,不可以用

p=a;

因为p的定义可以解读为:

int *(*p);

即指针p的关联类型为int*
而数组a的定义可以解读为:

 int(a[2])[3];

a是关联类型为int[3]的一维数组。很显然,a与p的关联类型都不一样,故而不能用p去操作a。在这举一个例子来说明:
如p+i的地址偏移量为
p+sizeof(int*) *i
而a+i的地址偏移量为

a+sizeof(int[4])*i

显然二级指针变量与二维数组名是不同的,我们必须定义一个指向一维数组的指针来操作逻辑上位二级指针的二维数组名。 二维数组的在计算机中的空间类型是以一维线性关系排布的,二维数组的指针使用方式本质上来说属于一维指针的调用方式。但是为了与一维指针相区别,所以必须调用两次*号运算符来接引用。但是二维数组指针与二重指针并不一样。 在未加*号之前 a的跨度为(int的大小列数),在第一次解引用之后*a的跨度为(int 的大小)比如

a[2][4]**(a+1)+3,**(a+7

都是对a[2][4]的最后一个元素(第二行第四列)进行访问的操作。
在这里插入图片描述
其实※a 和a 指向的地址是一模一样的,只是编译器理解的含义不用,对于a编译器会将其看成是一个指向数组的指针,而*a编译器则会将其看成某一具体数组的首地址。
*a

zhong点来了:
对于使用指针访问2维数组的话,
``

Int a[2][4]={1,2,3,4,5,6,7,8};
 Int **p=(int **)a;
 Cout<< *(*(p+1)+2);
 

这种方法是错误的,不可以使用二维指针直接访问,即使p和a的指向地址相同也不可!
但是如下方法又是可行的:

 Int a[2][4]={1,2,3,4,5,6,7,8};
 Int **p=(int **)a;
 Cout<< (int (*)[4])p;
 

只要你进行类型转换告诉编译器这是个指向4维数组的指针,编译器就可以正确的解码了。这是为什么呢?,
上文我说过:二维数组本质上其实是一维指针。所以只能进行一次物理地址的访问。*a和a指向的是一个地址,第一次取*号其实不是真正的去访问物理地址,只不过的编译器看他的方式改变了(对于a编译器会将其看成是一个指向数组的指针,而a编译器则会将其看成某一具体数组的首地址),所以第二次取号才是真正的访问物理地址上数组的元素。
但是对于二维指针(int **)p来说,这是真正的二维指针,你对它取第一次*号的时候 *p已经真正的访问了物理地址,这时*p是a[2][4]中的第一个元素a[0][0],他的值为1。那么第二次取*的时候,**p就会要求访问的物理地址为1的单元,很明显这是我们的进程不能访问的地址段,所以程序出错。

  • 10
    点赞
  • 1
    评论
  • 30
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值