我正在搞乱多维数组和指针。我一直在看一个程序,它打印出一个简单数组的内容和地址。这是我的数组声明:
int zippo[4][2] = { {2,4},
{6,8},
{1,3},
{5,7} };
我目前的理解是zippo
是一个指针,它可以保存其他几个指针的地址。默认情况下,zippo
保存指针zippo[0]
的地址,并且还可以保存指针zippo[1]
,zippo[2]
和zippo[3]
的地址。
现在,请采取以下声明:
printf("zippo[0] = %p\n", zippo[0]);
printf(" *zippo = %p\n", *zippo);
printf(" zippo = %p\n", zippo);
在我的机器上,它提供以下输出:
zippo[0] = 0x7fff170e2230
*zippo = 0x7fff170e2230
zippo = 0x7fff170e2230
我完全理解为什么zippo[0]
和*zippo
具有相同的值。它们都是指针,它们都存储整数2的地址(默认情况下)或zippo[0][0]
。但是zippo
如何共享相同的内存地址呢? zippo
不应该存储指针zippo[0]
的地址吗? Whaaaat?
答案 0 :(得分:32)
当数组表达式出现在大多数上下文中时,其类型将从“N元素数组T”隐式转换为“指向T”,其值设置为指向数组中的第一个元素。此规则的例外情况是,数组表达式是sizeof
或地址 - (&
)运算符的操作数,或者当数组是在声明中用作初始值设定项的字符串文字时
因此,表达式zippo
“衰减”从类型int [4][2]
(int元素的2元素数组的4元素数组)到int (*)[2]
(指向int的2元素数组的指针) )。同样,zippo[0]
的类型为int [2]
,隐式转换为int *
。
鉴于声明int zippo[4][2]
,下表显示了涉及zippo和任何隐式转换的各种数组表达式的类型:
Expression Type Implicitly converted to Equivalent expression ---------- ---- ----------------------- --------------------- zippo int [4][2] int (*)[2] &zippo int (*)[4][2] *zippo int [2] int * zippo[0] zippo[i] int [2] int * &zippo[i] int (*)[2] *zippo[i] int zippo[i][0] zippo[i][j] int &zippo[i][j] int * *zippo[i][j] invalid
请注意,zippo
,&zippo
,*zippo
,zippo[0]
,&zippo[0]
和&zippo[0][0]
都具有相同的值;它们都指向数组的基数(数组的地址与数组的第一个元素的地址相同)。但是,各种表达的类型都不同。
答案 1 :(得分:31)
声明多维数组时,编译器会将其视为单维数组。多维数组只是一种让我们的生活更轻松的抽象。你有一个误解:这不是一个指向4个数组的数组,它总是只是一个连续的内存块。
在你的情况下,做:
int zippo[4][2]
与做
真的一样int zippo[8]
编译器为您处理2D寻址所需的数学。
有关详细信息,请参阅C ++中的tutorial on Arrays。
这与做的非常不同:
int** zippo
或
int* zippo[4]
在这种情况下,你要制作一个包含四个指针的数组,这些指针可以分配给其他数组。
答案 2 :(得分:5)
zippo
不是指针。这是一个数组值数组。 zippo
和zippo[i]
中i
的0 {4}可以在某些情况下“衰减”到指针(特别是在值上下文中)。尝试打印sizeof zippo
以获取在非值上下文中使用zippo
的示例。在这种情况下,sizeof
将报告数组的大小,而不是指针的大小。
数组的名称, in value contexts ,衰减到指向其第一个元素的指针。因此,在值上下文中,zippo
与&zippo[0]
相同,因此具有“指向int
的数组[2]”的类型; *zippo
,在值上下文中与&zippo[0][0]
相同,即“指向int
的指针”。它们具有相同的值,但类型不同。
我建议您阅读Arrays and Pointers来回答第二个问题。指针具有相同的“值”,但指向不同的空间量。尝试打印zippo+1
和*zippo+1
以便更清楚地看到:
#include <stdio.h>
int main(void)
{
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
printf("%lu\n", (unsigned long) (sizeof zippo));
printf("%p\n", (void *)(zippo+1));
printf("%p\n", (void *)(*zippo+1));
return 0;
}
对于我的跑步,打印:
32
0xbffede7c
0xbffede78
告诉我机器上的sizeof(int)
为4,而第二和第三个指针的值不相同(如预期的那样)。
此外,"%p"
格式说明符在void *
函数中需要*printf()
,因此您应该在void *
次调用中printf()
投射指针printf()
是一个可变函数,所以编译器不能在这里为你做自动转换。)
编辑:当我说数组“衰减”到指针时,我的意思是值上下文中数组的名称等同于指针。因此,如果某个类型T pt[100];
有T
,那么名称pt
在值上下文中的类型为T *
。对于sizeof
和一元&
运算符,名称pt
不会缩减为指针。但是你可以T *p = pt;
- 这完全有效,因为在这种情况下,pt
的类型为T *
。
请注意,这种“腐朽”只发生过一次。所以,假设我们有:
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
然后,值上下文中的zippo
衰减为类型的指针:指向int
的数组[2]的指针。在代码中:
int (*p1)[2] = zippo;
有效,而
int **p2 = zippo;
将触发“不兼容的指针分配”警告。
如上所述定义zippo
,
int (*p0)[4][2] = &zippo;
int (*p1)[2] = zippo;
int *p2 = zippo[0];
都有效。使用printf("%p\n", (void *)name);
打印时,它们应该打印相同的值,但指针的不同之处在于它们分别指向整个矩阵,一行和一个整数。
答案 3 :(得分:2)
这里重要的是int zippy[4][2]
与int **zippo
不是同一类型的对象。
就像int zippi[5]
一样,zippy
是内存块的地址。 但是编译器知道你要用{2}语法来解决从zippy
开始的八个内存位置,但想要从zippi
开始寻址五个内存位置一维语法。
zippo
完全不同。 它保存一块大到足以包含两个指针的内存块的地址,如果你将它们指向某些整数数组,你可以用二维取消引用它们数组访问语法。
答案 4 :(得分:2)
Reed非常好地解释,我将添加更多点以使其更简单,当我们引用zippo
或zippo[0]
或zippo[0][0]
时,我们仍然指的是相同的基础数组zippo
的地址。原因是数组总是连续的内存块,而多维数组是连续放置的多个单维数组。
当你必须按每行递增时,你需要一个指针int *p = &zippo[0][0]
,并且每行按p++
递增指针。
在你的示例id中,它是一个4 X 2数组,在执行p++
时,指针当前指向第二组4个元素。