C多维数组中的指针地址

时间:2010-01-05 02:14:35

标签: c arrays memory pointers 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?

5 个答案:

答案 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*zippozippo[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不是指针。这是一个数组值数组。 zippozippo[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非常好地解释,我将添加更多点以使其更简单,当我们引用zippozippo[0]zippo[0][0]时,我们仍然指的是相同的基础数组zippo的地址。原因是数组总是连续的内存块,而多维数组是连续放置的多个单维数组。

当你必须按每行递增时,你需要一个指针int *p = &zippo[0][0],并且每行按p++递增指针。 在你的示例id中,它是一个4 X 2数组,在执行p++时,指针当前指向第二组4个元素。