指向2D数组的指针(为什么它可以工作)

时间:2016-12-07 15:55:40

标签: c++ arrays pointers multidimensional-array

我有以下功能(我想打印给定行中的所有元素)

void print_row(int j, int row_dimension, int *p)
{   
p = p + (j * row_dimension);

for(int i = 0; i< row_dimension; i++)
    cout<<*(p+i)<< " ";
}

创建数组

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

我不明白为什么我可以通过以下方式调用该函数:

print_row(i, 3, *j);

为什么我可以作为参数提供&#34; * j&#34; ?不应该通过地址吗?为什么我可以使用间接运算符?

4 个答案:

答案 0 :(得分:3)

int j[3][3] = 
{{1,2,3},
{4,5,6},
{7,8,9}}; // 2d array

auto t1 = j; // int (*t1)[3]
auto t2 = *j; // int *t2

所发生的事情是*j生成j[0],这是一个int[3],然后衰减为int*

答案 1 :(得分:2)

您的代码有效,因为*j是一个与jj[0]具有相同值的指针。这种行为是由编译器如何处理二维数组的机制引起的。

声明2D数组时:

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

编译器实际上将所有值顺序放入内存中,因此以下声明将具有相同的占用空间:

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

因此,在您的情况下,指针j*jj[0]只是指向内存中的相同位置。

答案 2 :(得分:2)

j实际上是一个数组数组。因此,*j是一个由三个整数组成的数组,当用作右值时,它衰减到指向其第一个元素的指针,换句话说,它衰减为&amp; j [0] [0]。

然后在printrow中计算每个子阵列的第一个元素的起始地址 - 这是不太好的部分,我会稍后再回来。然后,您正确使用*(p+i)等效的p[i]来访问子数组的每个元素。

答案的其余部分是我对严格阅读C标准的解释

我说计算每个子阵列的起始地址是不太好的部分。它的工作原理是因为我们都知道大小为NxM的2D数组在内存中的表示形式与大小为N * M的线性数组相同,我们别名这些表示。但是如果我们尊重严格标准,作为一个int指针,&p[i][j]指向三个元素数组的第一个元素。因此,当您添加行的大小时,如果稍后取消引用此地址,则指向数组末尾会导致未定义的行为。当然它适用于所有常见的编译器,但在old question of mine上,@ HansPassant给了我一个关于能够对数组大小实施控制的实验编译器的参考。那些编译器可以检测到数组末尾的访问并引发运行时错误......但它会破坏很多现有代码!

要严格遵守标准符合,您应该使用指向3个整数数组的指针。它需要使用可变长度数组,这是一个可选功能,但完全符合标准,适用于支持它的系统。或者,您可以转到2D数组的 byte 表示,获取其初始地址,然后从那里计算作为字节地址每个子数组的起始点。这是很多沸腾的地址计算,但它完全尊重@#!%$ strict别名规则...

TL / DR:此代码适用于所有常见的编译器,并且可能适用于它们的许多未来版本,但在严格解释标准时它是不正确的。

答案 3 :(得分:1)

内存不是多维的,所以即使它是一个2D数组,它的数据也会以顺序方式放置,所以如果你得到一个指向该数组的指针 - 这是一个指向它的第一个元素的指针 - - 并按顺序开始读取元素,您将遍历此2D数组的所有元素,从后一行的最后一个元素之后的后续行中读取元素。