C/C++数组访问方式:*a,*a[0],*(*(a+i)+j)详解



一、数组的访问方式

1、下标访问

通过数组名和下标访问,这里不赘述。

2、指针访问

int arr[4][5] = 
	{ {1, 2, 3, 4, 5},
  	  {6, 7, 8, 9, 10},
  	  {11,12,13,14,15},
  	  {16,17,18,19,20} };

首先,二维数组的本质是数组的数组,即二维数组的每个元素都是一个一维数组。
例如一个二维数组 int arr[4][5],那么 arr就是一个包含了 4 个一维数组的数组,每个一维数组都有 5 个元素。

1)数组名的含义

需要区分:

  • arrarr[0]

在 C/C++中,数组名可以表示数组的首地址,也就是数组的第一个元素的地址。
例如,arr二维数组的名字,arr就表示二维数组arr首地址,也就是一维数组 arr[0]地址
同样,arr[0]一维数组的名字,arr[0]就表示一维数组 arr[0]首地址,也就是元素 arr[0][0]地址。即

  • arr等价于&arr[0]
  • arr[0]等价于&arr[0][0]

并且,对数组名取地址,就等于数组中第一个元素的值(或对象),即

  • *arr等价于arr[0]
  • *arr[0]等价于arr[0][0]

简单测试一下:

#include<iostream>
#include <typeinfo>  // 打印数据类型
using namespace std;

int main() {
	int arr[4][5] = 
		{ {1, 2, 3, 4, 5},
	  	  {6, 7, 8, 9, 10},
	  	  {11,12,13,14,15},
	  	  {16,17,18,19,20} };
	  	  
	cout << "arr = " << arr << endl;                   
	cout << "arr[0] = " << arr[0] << endl;
	cout << "&arr[0][0] = " << &arr[0][0] << endl;
	cout << endl;
	cout << "*arr = " << *arr << endl;                   
	cout << "*arr[0] = " << *arr[0] << endl;
	cout << "arr[0][0] = " << arr[0][0] << endl;
	
	return 0;
}

输出结果如下:

arr = 0x0057f6a0
arr[0] = 0x0057f6a0
&arr[0][0] = 0x0057f6a0

*arr = 0x0057f6a0
*arr[0] = 1
arr[0][0] = 1

注意:
虽然arrarr[0]&arr[0][0]打印出来值相同,但他们含义不同。可以查看他们各自的类型:
在这里插入图片描述
C语言中的类型是由编译器根据表达式的语法来确定的,而不是根据表达式的值来确定的。其中arrarr[0]表示的是数组名,而 &arr[0][0]由于&取地址符是一个指针,并且根据 C/C++规则,数组名的类型是由数组的元素类型和数组的长度决定的,因此就可以解释上述三个变量的类型。

2)数组地址和元素地址

需要区分:

  • arr*arr&arr
  • arr[i]*arr[i]&arr[i]
  • arr[i][0]&arr[i][0]
  1. arr&arr*arr
    前面讲到,arr为数组名,表示二维数组首元素地址,则*arr等价于arr[0]
    在这里插入图片描述
    &arr,对数组名取地址表示 整个二维数组的首地址,虽然在值上和arr相同,但是对其加减操作上是不同的。
    • &arr+1代表该二维数组最后一个元素的下个位置的地址,即比arrsizeof(arr) = 4*(4*5) = 80字节,跨过了整个二维数组。
    • arr+1表示arr[1]的地址,即比arrsizeof(arr[0]) = 4*5 = 20字节,可参考下一小节 “通过指针访问数组元素”
      在这里插入图片描述
      &arr+1从十六进制0x0057f6a00x0057f6f0,算一下,确实移动了80字节。
      &arr+2从十六进制0x0057f6a00x0057f740,移动了160字节。

可以理解为&arr是一个更大的数组(三维数组),这个数组是以arr这个二维数组作为其中一个元素,那么对&arr加 1 就是按照其中元素大小来移动指针地址的,可以参考下面 “通过指针访问数组元素” 这一小节。

  1. arr[i]&arr[i]*arr[i]
    同样,arr[i]作为数组名,表示一维数组的首地址,则 *arr[i]等价于 arr[i][0]
    (下面示例中用arr[0]举例)
    在这里插入图片描述
    同样,&arr[i]表示 整个一维数组的首地址,在值上和arr[i]相同,但是含义不同

    • &arr[i]+1代表该一维数组最后一个元素的下个位置的地址,即比arr[i]sizeof(arr[i]) = 4*5 = 20字节,跨过了整个一维数组。
    • arr[i]+1表示arr[i][1]的地址,即比arr[i]sizeof(arr[i][0]) = 4字节,可参考下一小节 “通过指针访问数组元素”
      在这里插入图片描述
      &arr[0]+1&arr[0]+2从十六进制0x0057f6a0分别到0x0057f6b40x0057f6c8,算一下,分别移动了20字节和40字节。
  2. arr[i][0]&arr[i][0]
    这个就比较好理解了,二维数组中arr[i][0]是一个int类型的值,&arr[i][0]则表示该值的地址。
    在这里插入图片描述

3)通过指针访问数组元素
i. 二维数组中的arr[i]

结合上一小节,如果我们想要表示二维数组arr的第 i 个元素,即一维数组arr[i],可以用 arr + i来表示arr[i]地址
这是因为,当我们对 数组名进行加法运算时,实际上是按照 数组元素的大小 来移动地址的

例如,对于int arr[4][5],如果arr的地址是 1000,那么 arr + 1的地址就是 1000 + 5 * 4 = 1020,也就是arr[1]的地址。同理,arr + 2 的地址就是1000 + 2 * 5 * 4 = 1032,也就是 arr[2] 的地址,其中 5*4 表示arr中一个元素的大小。

通过对以上地址解引用就可以获取相应的对象或者元素。即

  • arr + i等价于&arr[i]
  • *(arr + i)等价于arr[i]。通过对地址解引用可以获得地址所指向的对象,arr[i]是这个一维数组的数组名,代表的是这个一维数组首元素地址(或称首地址),等价于&arr[i][0]

因此,arr[i]本质上仍然是个地址,对&arr[i]解引用并不是得到某个具体类型的值,而是一个对象

如果不好理解,可以拿一维数组举例,例如 int a[5] = {1,2,3,4,5};a表示数组首地址,也是a[0]的地址,即a = &a[0]a+2表示a[2]的地址,即a + 2 = &a[2],那么两边同时解引用,则可以获取到a[2]的值,即*(a + 2) = a[2] = 3
这里和二维数组的区别在于,一维数组对a解引用后,直接获取元素的值,但是在二维数组中,对arr解引用后,是一个对象,再次解引用才是一个int类型的值。

ii. 二维数组中的arr[i][j]

然后来看看如何用指针来引用 arr[i][j]的值。我们已经知道了,arr[i]*(arr + i)都表示一维数组 arr[i]的值,而arr[i]本身又是一个数组名,也可以表示数组的首地址,即&arr[i][0]。因此有:arr[i]等价于&arr[i][0]等价于*(arr + i),也即arr[i][0]等价于*(*(arr + i))
上面说到:

当对数组名进行加法运算时,实际上是按照 数组元素的大小 来移动地址的。

那么,我们可以用 arr[i] + j来表示 arr[i][j]地址,同样也可以用 *(arr + i) + j来表示 arr[i][j]地址。即:

  • *(arr + i) + j等价于&arr[i][j]
  • *(*(arr + i) + j)等价于arr[i][j]

总结

总结一下,对于二维数组int arr[4][5],有以下的等价关系:

  • arr等价于&arr[0]*arr等价于arr[0]
  • arr[0]等价于&arr[0][0]*arr[0]等价于arr[0][0]
  • arr + i等价于&arr[i]*(arr + i)等价于arr[i]
  • *(arr + i) + j等价于&arr[i][j]*(*(arr + i) + j)等价于arr[i][j]

arr单独拿出来表示的是首元素的地址,而&arr是整个数组的地址。


参考:
二维数组的首地址、首行地址和元素地址
C语言学习之:一维数组、二维数组的取值和取地址问题
关于二维数组a[i][j]
为什么C语言中*(a+i)+j能表示a[i][j]的地址

  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值