我有以下功能(我想打印给定行中的所有元素)
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; ?不应该通过地址吗?为什么我可以使用间接运算符?
答案 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
是一个与j
或j[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
,*j
和j[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数组的所有元素,从后一行的最后一个元素之后的后续行中读取元素。