C语言中,a为一个二维数组,为什么(&a[0])[2] 与(&a[2])[0]输出的结果一样?

int a[3][4]{ 1,2,3,8,55,89,7,8,9,10,11,12 };//定义一个3行4列的整型数组
cout << "(&a[0])[2] = " << (&a[0])[2] << endl;
cout << "(&a[2])[0] = " << (&a[2])[0] << endl;
cout << "(&a[0])[1] = " << (&a[0])[1] << endl;
cout << "(&a[1])[0] = " << (&a[1])[0] << endl;

在C语言中,二维数组a在内存中的存储方式是按行优先(row-major)的方式存储的,也就是说a[0]存放在内存的第一块地址上,紧接着a[1]、a[2]……以此类推。
(&a[0])[2] 表示的是a[0]指向的地址再往后偏移2个a[0]大小的位置,即移动了两个一维数组的位置,所以等价于指向a[2]这个一维数组,再经过下标访问(&a[2])[0],即为a[2][0]。所以该表达式和(&a[2])[0]实际上访问的都是同一个内存地址上的值,因此输出结果相同。追问

“再经过下标访问(&a[2])[0],即为a[2][0]”这句没理解,(&a[2])[0] 这个不应该是等于&a[2][0]吗

追答

实际上,(&a[2])[0] 和 &a[2][0] 在大多数情况下的确是相等的,因为它们都指向数组 a 的第三个元素。这是由于 C 语言中的数组是一段连续的内存空间,并且在内存中排列顺序是按照行优先的方式进行的。因此,在 (&a[2])[0] 中,&a[2] 其实是指向 a[2] 这个元素的指针,即第三个元素的地址,而 [0] 则从该地址开始,取出第一个元素,即 a[2][0]。
值得一提的是,虽然 (&a[2])[0] 和 &a[2][0] 在大多数情况下相等,但由于数组的内存分配方式并没有在标准中明确定义,因此在某些特殊情况下,这两个表达式可能会有所区别。因此,在编写程序时,应尽量避免使用类似于 (&a[2])[0] 这样的表达式,以保证程序的可移植性和正确性。

温馨提示:答案为网友推荐,仅供参考
第1个回答  2023-04-17

在C语言中,a是一个3行4列的整型二维数组。当您使用数组名a时,它会解引用为指向数组第一个元素(即&a[0][0])的指针。在您的示例中,a是一个int[3][4]类型的数组,因此&a[0]将是一个指向数组a的第一行的指针,类型为(*int)[4]。

让我们一行一行地分析这些cout语句:

    cout << "(&a[0])[2] = " << (&a[0])[2] << endl;: &a[0]是指向第一行的指针,类型为(*int)[4]。(&a[0])[2]表示向后移动两个int[4]类型的单位,即跳过前两行,指向第三行。因此,(&a[0])[2]等价于a[2],它解引用为&a[2][0]。

    cout << "(&a[2])[0] = " << (&a[2])[0] << endl;: &a[2]是指向第三行的指针,类型为(*int)[4]。(&a[2])[0]表示不移动任何单位,所以它仍然指向第三行。因此,(&a[2])[0]等价于a[2],它解引用为&a[2][0]。

    这就是为什么(&a[0])[2]和(&a[2])[0]输出的结果相同。它们都解引用为&a[2][0],指向二维数组中第三行的第一个元素。

    而对于(&a[0])[1]和(&a[1])[0],它们分别解引用为&a[1][0]和&a[1][0],指向二维数组中第二行的第一个元素,因此它们的输出结果也是相同的。

追问

“(&a[2])[0]表示不移动任何单位,所以它仍然指向第三行。因此,(&a[2])[0]等价于a[2],”这句没看懂,如果仍然指向第三行,(&a[2])[0]应该等价于&a[2]吧?

追答

“(&a[2])[0]表示不移动任何单位,所以它仍然指向第三行。因此,(&a[2])[0]等价于a[2]”这句对是对,不过引出了更多的内容,a[2]是用数组的形式来表示a[3][4]的第三行,此处暂不理解可以跳过。
(&a[0])[2] 与(&a[2])[0]其实可以用大白话来解释,括号的作用是表示整行,(&a[0])[2] 就是第一行移两个单位的指针位置,(&a[2])[0]就是每三行不移的情况。