在二维数组A[m][n]中,A的值如何与*A相同?

In 2D array A[m][n], how value of A is same as *A?

我对二维数组的了解:

  1. 在数组中,数组名是指向第一个元素地址的指针
  2. 这里我们可以把A看作数组的数组,所以A会指向第0个一维数组
  3. 所以A+i会指向A的第i个元素
  4. *(A+i) 将指向 A
  5. 的第 i 个元素的第一个元素
  6. 那么在二维数组中A+i的地址值应该和*(A+i)一样

但这对我来说不明白 A 值与 *A 的值如何相同,谁能告诉我这是如何在内存中工作的,我知道这是正确的但我无法向自己解释

声明

In array the array name is pointer to first element's address

错误的

对于任何数组,它的符号本身会衰减指向它第一个元素的指针。

所以对于你的数组 A 它将衰减到 &A[0]

如果您取消引用该指针,就像 *A 所发生的那样,那么您将得到 *(&A[0])。这与普通 A[0].

相同

既然你的数组 A 是一个数组的数组,那么 A[0] 也是一个数组,它反过来也会衰减到指向它的第一个元素的指针。所以 A[0] 会衰减到 &A[0][0].

因此 *A 将与 &A[0][0] 相同。

然而,对于不同的指针,类型有很大的不同。

以您的示例数组为例:

int A[3][4];

那么 &A[0] 将是一个指向四个 int 值数组的指针,或 int (*)[4]&A[0][0] 将是指向单个 int 值的指针,或 int *.


现在说说为什么所有这些指针看起来都一样,是因为它们都指向同一个位置,而这个位置恰好与数组本身的位置相同(即 &A,将具有类型 int (*)[3][4]).

如果我们“绘制”它,它看起来会像这样:

+---------+---------+---------+---------+---------+---------+-----+
| A[0][0] | A[0][1] | A[0][2] | A[0][3] | A[1][0] | A[1][1] | ... |
+---------+---------+---------+---------+---------+---------+-----+
^
|
&A
|
&A[0]
|
&A[0][0]

如您所见,所有三个指针都将指向相同的位置,但如前所述,它们具有不同的类型。

“在数组中,数组名称是指向第一个元素地址的指针。”那不是真的。是时候忘记你曾经听过它了。这部分是正确的,在某些有限的情况下它可能是一个有用的解释,但迟早会引起比它可能提供的任何有用解释更多的混乱。

真实情况是这样的:当您在表达式中请求数组的“值”时,您得到的是指向其第一个元素的指针。

所以对于任何数组 A,如果你尝试像这样打印它的值:

printf("%p\n", A);

您将看到指向数组第一个元素的指针。

但是你有一个二维数组。所以如果你要求 A,你会得到一个指向数组第一个元素的指针。但是当你请求 *A 时会发生什么?

嗯,如果 A 给你一个指向数组第一个元素的指针,那么 *A 给你那个指针的“内容”, 数组的第一个元素,也就是……另一个数组!

如果您尝试获取“'that'”数组的值,请说

printf("%p\n", *A);

你得到的是指向那个数组第一个元素的指针。

我想你可以看到,对于二维数组,指向整个数组的指针与指向数组第一行的指针相同,后者与指向数组第一行的第一个元素。


其余部分一开始可能有点令人困惑。我说,“当你在表达式中请求数组的“值”时,你得到的是指向它第一个元素的指针。”更正式地说,这意味着在表达式中,当您引用 A 时,它与您所说的 &A[0].

100% 完全相同

因此,由于 * 有点取消 & 的效果,当您说 *A 时,它 100% 完全和您说 [= 一样22=].

以上内容适用于任何类型的数组。对于二维数组,事情会变得更有趣。

首先,对于二维数组,当我们查看 *AA[0] 时,其中任何一个都指的是 另一个 数组 - - 二维数组的第一行。

因此 A 为您提供指向 A 的第一个元素的指针,这是另一个数组。
*A 为您提供指向第一行第一个元素的指针,这是一个实际的单元格。
所以表达式A*A会有相同的指针值,但是它们有不同的类型!
第一个的类型是“指向任意数组的指针”,而第二个的类型是“指向任意的指针”。

另见 question 6.12 in the old C FAQ list

如果您有一个数组,那么它在表达式中使用的指示符(例如用作 sizeof 运算符的操作数)将转换为指向其第一个元素的指针。

如何为多维数组正确编写这样的指针?

假设您有一个多维数组,例如

T a[N1][N2][N3][N4];

其中 T 是某种类型,N1N2N3N4 是子数组中元素的数量。然后要获得指向数组元素类型的指针,您可以像

这样重写数组
T ( a[N1] )[N2][N3][N4];

因此,要获取指针,只需将记录 a[N1] 替换为记录 *p,如

T ( a[N1] )[N2][N3][N4];
T ( *p )[N2][N3][N4] = a;

现在指针 p 指向数组 a 的第一个类型为 T [N2][N3][N4] 的元素。

这里有一些例子

T ( a[N1] );
T ( *p ) = a; // that can be simplified like T *p = a;

T ( a[N1] )[N2];
T ( *p )[N2] = a;

T ( a[N1] )[N2][N3];
T ( *p )[N2][N3] = a;

等等。

In array the array name is pointer to first element's address

C 标准(6.3.2.1 左值、数组和函数指示符)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

Here we can think A as array of array, so A would point to 0th 1D array

是的,多维数组是数组元素依次为数组。

So A+i would point to ith element of A

是的,在表达式 A + i 中,数组指示符被转换为指向其第一个元素的指针。因此使用指针算法,源表达式指向数组的第 i 个元素。

*(A+i) would point to first element of ith element of A

表达式 *( A + i ) 产生表达式 A + i 指向的对象的左值。如果 A 是二维数组,则表达式 *( A + i ) 生成位于源数组第 i 个“行”中的一维数组。表达式 *( A + i ) 等价于表达式 A[i].

Then in 2D array A+i address value should be same as *(A+i)

A + i 是指向数组 A 的第 i 个元素的 a。如果 A 是二维数组,则表达式 *( A + i ) 产生数组的第 i 行,即 a一维数组。在表达式中,一维数组的这个指示符 *( A + I ) 又被转换为指向其第一个元素的指针。所以这两个指针 A + i*( A + i ) 在隐式转换后最后一个指向指针的表达式将具有相同的值但类型不同。

所以如果你有

T A[N1][N2];

则表达式 A + i 的类型为 T( * )[N2]。取消引用像 *( A + i ) 这样的表达式,您将得到原始数组的第 i 个元素,它是 T[N2] 类型的一维数组。反过来,表达式中使用的这个数组指示符被转换为类型 T * 的第一个元素,并且两个指针 T( * )[N2]T * 将在原始数组占用的内存范围内具有相同的地址

这是一个演示程序。

#include <stdio.h>

int main(void) 
{
    enum { N1 = 3, N2 = 5 };
    int ( a[N1] )[N2];
    int ( * p )[N2] = a;
    
    for ( size_t i = 0; i < N1; i++)
    {
        printf( "a + %zu = %p, p + %zu = %p\n", 
                i, ( void * )( a + i ), i, ( void * )( p + i ) );
        printf( "*( a + %zu ) = %p, *(p + %zu ) = %p\n\n", 
                i, ( void * )*( a + i ), i, ( void * )*( p + i ) );
    }
    
    return 0;
}

它的输出可能看起来像

a + 0 = 0x7ffda1063ab0, p + 0 = 0x7ffda1063ab0
*( a + 0 ) = 0x7ffda1063ab0, *(p + 0 ) = 0x7ffda1063ab0

a + 1 = 0x7ffda1063ac4, p + 1 = 0x7ffda1063ac4
*( a + 1 ) = 0x7ffda1063ac4, *(p + 1 ) = 0x7ffda1063ac4

a + 2 = 0x7ffda1063ad8, p + 2 = 0x7ffda1063ad8
*( a + 2 ) = 0x7ffda1063ad8, *(p + 2 ) = 0x7ffda1063ad8

注意表达式a + 0的值比表达式a + 1的值小20(或十六进制0x14)因为sizeof( *( a + i ) )sizeof( int[5] ) 相同等于 20.