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)=16;
	int * q2[4];	//定义了一个数组 里面存有int * 类型的指针 跨度为sizeof(int *)

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

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

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

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

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

二维数组的在计算机中依然是以一维线性关系排布的

以二维数组a[2][3]为例,a是由数组a[0]、a[1]组成的一维数组,因此,数组名a可以看作是数组 { a[0], a[1] } 的首地址,即a==&a[0]。

定义一个二维数组和二级指针变量

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 = p+4*i Byte //假定计算机中的用4个字节Byte来存储指针变量

而a+i的地址偏移量为

a+sizeof(int[3])*i = p+3*4*i Byte

显然二级指针变量与二维数组名的跨度是不同的,所以赋值会导致编译器不能确定其跨度,因为编译器选择报错。 二维数组的在计算机中的空间类型是以一维线性关系排布的,二维数组的指针使用方式本质上来说属于一维指针的调用方式。但是为了与一维指针相区别,所以必须调用两次*号运算符来接引用。但是二维数组指针与二重指针并不一样。 在未加*号之前 a的跨度为(int的大小×列数),在第一次解引用之后*a的跨度为(int 的大小)比如

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

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

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

Int a[2][4]={12345678};
 Int **p=(int **)a;
 Cout<< *(*(p+1)+2);
 

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

 Int a[2][4]={12345678};
 Int **p=(int **)a;
 Cout<< (int (*)[4])p;

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

  • 15
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值