如何解决在二维数组 A[m][n] 中,A 的值如何与 *A 相同?
我对二维数组的了解:
- 在数组中,数组名是指向第一个元素地址的指针
- 这里我们可以将 A 视为数组的数组,因此 A 将指向第 0 个一维数组
- 所以 A+i 将指向 A 的第 i 个元素
- *(A+i) 将指向 A 的第 i 个元素的第一个元素
- 那么在二维数组中 A+i 地址值应该与 *(A+i) 相同
但这对我来说没有意义 A 值如何与 *A 相同,有人可以告诉我这是如何在内存中工作的,我知道这是正确的,但我无法向自己解释
解决方法
声明
在数组中,数组名是指向第一个元素地址的指针
错误。
对于任何数组,它的符号本身会衰减到指向它的第一个元素的指针。
因此对于您的数组 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
会为您提供该指针的“内容”,它是数组的第一个元素数组,也就是……另一个数组!
如果您尝试获取“那个”数组的值,请说
printf("%p\n",*A);
你得到的是一个指向那个数组第一个元素的指针。
我认为你可以看到,对于二维数组,指向整个数组的指针将与指向数组中第一行的指针相同,与指向第一个元素的指针相同数组的第一行。
接下来的内容一开始可能有点令人困惑。我说,“当你在表达式中要求数组的“值”时,你得到的是一个指向它的第一个元素的指针。更正式地说,这意味着在表达式中,当您提到 A
时,它 100% 完全相同,就像您说的 &A[0]
一样。
因此,由于 *
有点取消 &
的效果,所以当您说 *A
时,它 100% 完全相同,就像您说 {{1} }.
以上适用于任何类型的数组。对于二维数组,事情会变得更有趣。
首先,对于二维数组,当我们查看 A[0]
或 *A
时,它们中的任何一个都引用了另一个数组——二维数组数组的第一行。
所以 A[0]
为您提供了一个指向 A
的第一个元素的指针,该元素是另一个数组。
而 A
会为您提供一个指向第一行第一个元素的指针,该元素是一个实际的单元格。
因此表达式 *A
和 A
将具有相同的指针值,但它们具有不同的类型!
第一个类型为“指向任何数组的指针”,而第二个类型为“指向任何内容的指针”。
另见旧 question 6.12 中的 C FAQ list。
,如果您有一个数组,那么它在表达式中使用的指示符(例如用作 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
指向数组 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;
等等。
在数组中,数组名是指向第一个元素地址的指针
C 标准(6.3.2.1 左值、数组和函数指示符)
3 除非是 sizeof 运算符或一元 & 的操作数 运算符,或者是用于初始化数组的字符串文字, 类型为“类型数组”的表达式被转换为 类型为“指向类型的指针”的表达式,指向首字母 数组对象的元素并且不是左值。如果数组对象 有寄存器存储类,行为未定义。
这里我们可以认为A是数组的数组,所以A会指向第0个1D 数组
是的,多维数组是数组元素依次是数组。
所以 A+i 将指向 A 的第 i 个元素
是的,在表达式 A + i
中,数组指示符被转换为指向其第一个元素的指针。所以使用指针算法,源表达式指向数组的第 i 个元素。
*(A+i) 将指向 A 的第 i 个元素的第一个元素
表达式 *( A + i )
产生表达式 A + i
指向的对象的左值。如果 A 是二维数组,则表达式 *( A + i )
生成位于源数组的第 i“行”中的一维数组。表达式 *( A + i ) 等价于表达式 A[i]
。
那么在二维数组中 A+i 地址值应该与 *(A+i) 相同
A + i 是指向数组 A 的第 i 个元素。如果 A 是二维数组,则表达式 *( A + i )
生成数组的第 i 行,该行是一个-维数组。在表达式中使用的这个一维数组的指示符 *( 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 ),( void * )( p + i ) );
printf( "*( a + %zu ) = %p,*(p + %zu ) = %p\n\n",( void * )*( a + 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
),因为 {{1 }} 等于 sizeof( *( a + i ) )
等于 sizeof( int[5] )
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。