二维数组如何存储在内存中?

问题描述 投票:0回答:5
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[101][101];
    a[2][0]=10;
    cout<<a+2<<endl;
    cout<<*(a+2)<<endl;
    cout<<*(*(a+2));
    return 0;
}

为什么

a+2
*(a+2)
的值相同?

提前致谢!

c++ arrays c++11 pointers multidimensional-array
5个回答
8
投票

a
是一个二维数组,即数组的数组。但当在适当的上下文中使用时,它会“衰减”为指向数组的指针。所以:

    a+2
  • 中,
    a
    衰减为指向大小为 101 的 int 数组的指针。当您将 is 传递给 ostream 时,您将获得该数组的第一个元素的地址,即
    &(a[2][0])
  • *(a+2)
  • 中的
    根据定义是
    a[2]:它是一个从
    a[2][0]
    开始、大小为101的数组。它衰减为一个指向 int 的指针,当你将它传递给 ostream 时,你会得到它的第一个元素的地址,它仍然是
    &(a[2][0])
  • **(a+2)
  • 根据定义
    a[2][0]。当你将它传递给 ostream 时,你会得到它的 int 值,这里是 10。
    
    
    
  • 但要注意:
a + 2

a[2]
都是指向同一地址的指针(
static_cast<void *>(a+2)
static_cast<void *>(a[2])
相同),但它们是指向不同类型的指针:第一个指向大小为101的int数组,后者指向int.
    


2
投票

让我们考虑一个更实用的多维数组示例:

int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

可以执行命令

x/10w a

在GDB中查看内存:

0x7fffffffe750: 1 2 3 4 0x7fffffffe760: 5 6 7 8 0x7fffffffe770: 9 0

每个元素都存储为 
int

类型(32 位/4 字节)。 所以矩阵的第一个元素已存储在: 1) a[0][0] -> 0x7fffffffe750 2) a[0][1] -> 0x7fffffffe754 3) a[0][2] -> 0x7fffffffe758 4) a[1][0] -> 0x7fffffffe75c 5) a[1][1] -> 0x7fffffffe760 6) a[1][2] -> 0x7fffffffe764 7) a[2][0] -> 0x7fffffffe768 ...

命令:

std::cout << a + 2 << '\n'

它将打印地址 
0x7fffffffe768

因为 指针算术: a 的类型是 int** 所以它是一个指向指针的指针。 a+2 是 a[0](第一行)+ 2。结果是一个指针 到第三排。 *(a+2) 遵循第三行,即 {7,8,9}

第三行是一个int数组,它是一个指向int的指针。

然后是操作员

<< will print the value of that pointer.


1
投票

char v[2][3] = {{1,3,5},{5,10,2}}; Content: | 1 | 3 | 5 | 5 | 10 | 2 Address: v v+1 v+2 v+3 v+4 v+5

要访问 v[x][y],编译器将其重写为: 
*(v + y * M + x)

(其中 M 是指定的第二个维度)


例如,要访问 v[1][1],编译器将其重写为

*(v + 1*3 + 1)

=>

*(v + 4)

请注意,这与指向指针 (char**) 的指针不同。 指向指针的指针不是数组:它包含指向内存单元的地址,该内存单元包含另一个地址。

要使用指向指针的指针访问二维数组的成员,请执行以下操作: char **p; /* Initialize it */ char c = p[3][5];

前往
p
内容指定的地址;
  1. 将偏移量添加到该地址(在我们的例子中为 3);
    转到该地址并获取其内容(我们的新地址)。
  2. 将第二个偏移量添加到新地址(在我们的例子中为 5)。
  3. 获取该地址的内容。
  4. 通过传统的二维数组访问成员时,步骤如下:
  5. char p[10][10]; char c = p[3][5];

获取
p
的地址,并将第一个偏移量 (3) 乘以行的尺寸 (10)。
  1. 将第二个偏移量 (5) 添加到结果中。
    获取该地址的内容。
  2. 如果你有一个这样的数组

1
投票

然后数组的名称会隐式转换为指向其第一个元素的指针,但极少数例外(例如在

sizeof
 运算符中使用数组名称)。

因此,例如在表达式

( a + 2 )

a

中,转换类型

T *
的值为
&a[0]

相对于您的数组示例

int a[101][101];

表达中

a + 2

a 转换为

int ( * )[101]
 类型的右值并指向数组的第一“行”。 
a + 2

指向数组的第三“行”。行的类型是

int[101]

表达式
*(a+2)
给出了类型为

int[101]

的第三行,它是一个数组。这个数组在表达式中使用时会依次转换为指向其第一个类型为

int *
的元素的指针。

与第三行占用的内存区域起始地址相同

只有表达式

( a + 2 )

具有类型

int ( * )[101]

,而表达式

*( a + 2 )
具有类型
int *
。但两者产生相同的值 - 数组第三行占用的内存区域的起始地址
a
    
数组的第一个元素与数组本身位于同一位置 - 数组中没有“空白空间”。


0
投票
cout << a + 2

中,

a

被隐式转换为指向其第一个元素

&a[0]
的指针,而
a + 2
a
的第三个元素
&a[2]
的位置。

cout << *(a + 2)
中,数组

*(a + 2)

- 即

a[2]
- 被转换为指向其第一个元素
&a[2][0]
的指针。

由于
a
的第三个元素的位置和

a

的第三个元素的第一个元素的位置相同,因此输出相同。

    

© www.soinside.com 2019 - 2024. All rights reserved.