指针地址在Cmultidimensional array中
我搞乱了multidimensional array和指针。 我一直在看一个程序,打印出一个简单的数组的内容和地址。 这是我的数组声明:
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?
当你声明一个multidimensional array时,编译器将它视为一个单维数组。 multidimensional array只是一个抽象,让我们的生活更轻松。 你有一个误解:这不是一个指向4个数组的数组,它总是只有一个连续的内存块。
在你的情况下,做:
int zippo[4][2]
真的和做一样
int zippo[8]
由编译器为您处理2D寻址所需的math。
有关详细信息,请参阅C ++中的数组本教程 。
这和做的很不一样:
int** zippo
要么
int* zippo[4]
在这种情况下,你正在创build一个四个指针的数组,这个指针可以被分配给其他数组。
当数组expression式出现在大多数上下文中时,它的types被隐式地从“T的N元素数组”转换为“指向T的指针”,并且它的值被设置为指向数组中的第一个元素。 这个规则的例外是当数组expression式是sizeof
或者( &
)操作符的地址的操作数,或者当数组是一个string文字被用作声明中的初始化器时。
因此,expression式zippo
从int [4][2]
( int [4][2]
元素数组的4元素数组)到int (*)[2]
(指向2元素数组的int)的“衰变”。 类似地, zippo[0]
的types是int [2]
,隐式转换为int *
。
给定int zippo[4][2]
的声明,下表显示了涉及zippo和任何隐式转换的各种数组expression式的types:
expression式types隐式转换为等效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]无效
请注意, zippo
, &zippo
, *zippo
, zippo[0]
, &zippo[0]
和&zippo[0][0]
都具有相同的值; 它们都指向数组的底部(数组的地址与数组的第一个元素的地址相同)。 各种expression式的types都不同。
zippo
不是一个指针。 这是一个数组值的数组。 zippo
和zippo[i]
for i
in 0..4在某些情况下(特别是在值上下文中)可以“衰减”到指针。 尝试打印sizeof zippo
作为在非值上下文中使用sizeof zippo
的示例。 在这种情况下, sizeof
将报告数组的大小,而不是指针的大小。
在值上下文中 ,数组的名称衰减为指向其第一个元素的指针。 所以,在值上下文中, zippo
与&zippo[0]
是一样的,因此它的types是“指向int
的数组[2]”。 *zippo
在值上下文中与&zippo[0][0]
,即“指向int
指针”。 他们有相同的价值,但不同的types。
我build议阅读数组和指针来回答你的第二个问题。 指针具有相同的“值”,但指向不同的空间量。 尝试打印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"
格式说明符在*printf()
函数中需要void *
,因此您应该在printf()
调用中将指针转换为void *
( printf()
是可变参数函数,因此编译器无法执行自动转换为你在这里)。
编辑 :当我说一个数组“衰变”的指针,我的意思是在值上下文中的数组的名称相当于一个指针。 因此,如果我有T pt[100];
对于某些typesT
,那么名称pt
在值上下文中是T *
types的。 对于sizeof
和一元&
运算符,名称pt
不会减less到指针。 但是你可以做T *p = pt;
这是完全有效的,因为在这种情况下, pt
是T *
types的。
请注意,这个“衰减”只发生一次。 所以,假设我们有:
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
然后,值上下文中的zippo
衰变为types指针:指向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);
打印时,它们应该打印相同的值printf("%p\n", (void *)name);
,但指针是不同的,它们分别指向整个matrix,一行和一个整数。
这里最重要的是int zippy[4][2]
与int **zippo
不是同一types的对象。
就像int zippi[5]
, zippy
是一块内存的地址。 但是编译器知道你想要用zippy
从二维语法开始寻址八个内存位置,但是要用一维语法来解决从zippi
开始的五个内存位置。
zippo
是完全不同的东西。 它保存了一个足够大的内存块的地址,以包含两个指针,如果使它们指向某些整数数组,则可以使用二维数组访问语法对它们进行解引用。
Reed解释得很好,我们再加几点来简化一下,当我们引用zippo
或者zippo[0]
或者zippo[0][0]
,我们仍然指向zippo
数组的相同的基地址。 原因是数组总是连续的内存块,而multidimensional array是连续放置的多个单维数组。
当你必须增加每一行时,你需要一个指针int *p = &zippo[0][0]
,并且通过每一行p++
增加指针。 在你的示例id中它是一个4×2的数组,在做p++
的时候,指针指向的是第二组4个元素。