我对2D阵列的了解:
- 在数组中,数组名称是指向第一个元素地址的指针
- 这里我们可以把A看作数组的数组,所以A会指向第0个1D数组
- 所以A+i会指向A的第i个元素
- *(A+i)将指向A的第i个元素的第一个元素
- 则在2D阵列中,A+i地址值应与*(A+i)相同
但这对我来说没有意义,A值与*A的值是如何相同的,有人能告诉我这在内存中是如何工作的吗?我知道这是正确的,但我无法向自己解释
语句
在数组中,数组名称是指向第一个元素地址的指针
是错误的。
对于任何数组,其符号本身将衰减到指向其第一个元素的指针。
因此,对于您的数组A
,它将衰减为&A[0]
。
如果你取消引用那个指针,就像*A
发生的那样,那么你就有了*(&A[0])
。与普通CCD_ 5相同。
由于数组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]
)。
如果我们";draw";它看起来像这样:
+---------+---------+---------+---------+---------+---------+-----+|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("%pn", A);
您将看到一个指向数组第一个元素的指针。
但是你有一个二维数组。因此,如果你要求A
,你会得到一个指向数组第一个元素的指针。但是当你要求*A
时会发生什么呢?
好吧,如果A
给你一个指向数组第一个元素的指针,那么*A
就会给你";内容";的指针,是数组的第一个元素,它是…另一个数组!
如果你试图通过说来获取"这个"数组的值
printf("%pn", *A);
得到的是指向数组的第一个元素的的指针。
正如我所看到的,对于二维数组,指向整个数组的指针将与指向数组中第一行的指针相同,这将与指向阵列中第一行第一个元素的指针相同。
其他内容一开始可能有点令人困惑。我说;当你要求";值";对于表达式中的数组,您得到的是指向其第一个元素的指针"更正式地说,这意味着在一个表达式中,当你提到A
时,它与你所说的&A[0]
是100%完全相同的。
因此,由于*
在某种程度上抵消了&
的影响,当你说*A
时,它与你说的A[0]
完全相同。
以上内容适用于任何类型的数组。对于二维数组,事情会变得更有趣。
首先,对于二维数组,当我们查看*A
或A[0]
时,其中任何一个都指另一个数组——二维数组的第一行。
因此,A
为您获取一个指向A
的第一个元素的指针,它是另一个数组*A
为您获取指向第一行的第一个元素的指针,该元素是一个实际的单元格
因此表达式A
和*A
将具有相同的指针值,但它们具有不同的类型
第一个具有类型"指向任意数组的指针";,而第二个具有类型";指针指向任何";。
另请参阅旧的C常见问题列表中的问题6.12。
如果有一个数组,则在表达式中使用的指示符(例如用作sizeof
运算符的操作数)将转换为指向其第一个元素的指针。
如何为多维数组正确写入这样的指针?
假设你有一个像这样的多维数组
T a[N1][N2][N3][N4];
其中T
是某种类型,N1
、N2
、N3
、N4
是子阵列中的元素数量。然后,要获得指向数组元素类型的指针,可以像一样重写数组
T ( a[N1] )[N2][N3][N4];
因此,要获得指针,只需将记录a[N1]
替换为类似的记录*p
T ( a[N1] )[N2][N3][N4];
T ( *p )[N2][N3][N4] = a;
现在,指针p
指向具有类型T [N2][N3][N4]
的数组a的第一个元素。
以下是的一些例子
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;
等等
在数组中,数组名称是指向第一个元素地址的指针
C标准(6.3.2.1值、数组和函数指示符)
3除非它是sizeof运算符或一元&运算符,或者是用于初始化数组的字符串文字具有类型为"的数组的表达式转换为类型为"的表达式指向指向首字母的类型"元素,并且不是左值。如果数组对象具有寄存器存储类,行为未定义。
这里我们可以认为A是数组的数组,所以A会指向第0个1D阵列
是的,多维数组是一个数组元素依次为数组的数组。
因此A+i将指向的第i个元素
是的,在表达式A + i
中,数组指示符被转换为指向其第一个元素的指针。因此,使用指针算术,源表达式指向数组的第i个元素。
*(A+i)将指向的第i个元素的第一个元素
表达式*( A + i )
产生由表达式A[0]
0指向的对象的左值。如果A是二维阵列,则表达式CCD_;行";源数组的。表达式*(A+i)等效于表达式A[i]
。
然后在2D阵列中A+i地址值应与*(A+i)相同
A+i是指向数组A的第i个元素的。如果A是二维数组,则表达式*( A + i )
产生数组的第i行,该行是一维数组。在表达式中,一维数组的这个指示符*( A + I )
又被转换为指向其第一个元素的指针。因此,这两个指针A + i
和*( A + i )
在将最后一个表达式隐式转换为指针后将具有相同的值但不同的类型。
所以如果你有
T A[N1][N2];
则表达式CCD_ 57具有类型CCD_。取消引用类似*( 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 = %pn",
i, ( void * )( a + i ), i, ( void * )( p + i ) );
printf( "*( a + %zu ) = %p, *(p + %zu ) = %pnn",
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( int[5] )
相同的sizeof( *( a + i ) )
等于20
。