C多维数组中的指针地址

Posted

技术标签:

【中文标题】C多维数组中的指针地址【英文标题】:Pointer address in a C multidimensional array 【发布时间】:2011-01-01 12:05:07 【问题描述】:

我在搞乱多维数组和指针。我一直在研究一个打印出简单数组的内容和地址的程序。这是我的数组声明:

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] 的地址吗?什么?

【问题讨论】:

其实我从来没有接触过Java。我用 BASIC 和 pascal 简单地把我的脚趾放到水里,然后直接用 C 跳了进去。C 是我真正尝试学习的第一个也是唯一一个语言。什么,Java 是更高级别的,因此不需要处理这种级别的细节? 【参考方案1】:

当数组表达式出现在大多数上下文中时,它的类型会隐式地从“T 的 N 元素数组”转换为“指向 T 的指针”,并且它的值被设置为指向数组中的第一个元素。此规则的例外情况是,当数组表达式是 sizeof 或地址 (&) 运算符的操作数时,或者当数组是在声明中用作初始值设定项的字符串文字时。

因此,表达式zippo“衰减”从int [4][2](int 的2 元素数组的4 元素数组)到int (*)[2](指向int 的2 元素数组的指针)。同样zippo[0]的类型为int [2],隐式转换为int *

鉴于声明int zippo[4][2],下表显示了涉及zippo的各种数组表达式的类型以及任何隐式转换:

表达式类型隐式转换为等效表达式 ---------- ---- ------------------------ ------------- -------- zippo int [4][2] int (*)[2] &zippo int (*)[4][2] *zippo 整数 [2] 整数 * zippo[0] zippo[i] 整数 [2] 整数 * &zippo[i] 整数 (*)[2] *zippo[i] int zippo[i][0] zippo[i][j] 整数 &zippo[i][j] 整数 * *zippo[i][j] 无效

请注意,zippo&zippo*zippozippo[0]&zippo[0]&zippo[0][0] 都具有相同的值;它们都指向数组的基数(数组的地址与数组的第一个元素的地址相同)。但是,各种表达式的类型都不同。

【讨论】:

这真的有助于我理解一个数组或指针有多少种表示方式,以及声明衰减意味着什么。非常彻底。谢谢。【参考方案2】:

当您声明一个多维数组时,编译器会将其视为一维数组。多维数组只是使我们的生活更轻松的抽象。您有一个误解:这不是一个指向 4 个数组的数组,它始终只是一个连续的内存块。

在你的情况下,做:

int zippo[4][2]

真的和做的一样

int zippo[8]

编译器为您处理二维寻址所需的数学运算。

有关详细信息,请参阅 C++ 中的 tutorial on Arrays。

这与做有很大不同:

int** zippo

int* zippo[4]

在这种情况下,您正在创建一个包含四个指针的数组,可以将其分配给其他数组。

【讨论】:

啊,这个解释很清楚了。谢谢。我将阅读一些简单的多维数组(作为单个连续的内存块)和指针数组之间的区别。【参考方案3】:

zippo 不是指针。它是一个数组值数组。 zippozippo[i] 用于 0..4 中的 i 在某些情况下(特别是在值上下文中)可以“衰减”到指针。尝试打印 sizeof zippo 以获取在非值上下文中使用 zippo 的示例。在这种情况下,sizeof 将报告数组的大小,而不是指针的大小。

数组的名称,在值上下文中,衰减为指向其第一个元素的指针。因此,在值上下文中,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" 格式说明符在*printf() 函数中需要void *,因此您应该在printf() 调用中将指针转换为void *printf() 是一个可变参数函数,因此编译器可以' t 在这里为您进行自动转换)。

编辑:当我说数组“衰减”为指针时,我的意思是值上下文中数组的名称等价于指针。因此,如果我有某个类型 TT pt[100];,那么名称 pt 在值上下文中属于 T * 类型。对于sizeof 和一元&amp; 运算符,名称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); 打印时,它们应该打印相同的值,但指针不同,它们分别指向整个矩阵、一行和一个整数。

【讨论】:

让我们看看我是否有这个权利:即使zippozippo[0]指向不同的内存量,它们的起始地址是相同的,并且在值上下文中它们都将指向 zippo 中的第一个整数。正确的?而且,这就是你所说的“衰变”吗? 没有。值上下文中的zippo[0]int *,并指向第一个整数,其中值上下文中的zippo 是“指向int 的数组[2] 的指针”。你可以这样做:例如int (*p)[2]; p = zippo;。然后,p[0] == 2p[1] == 4。对于“衰减”,请参阅我的(即将出现的)编辑。【参考方案4】:

这里重要的是int zippy[4][2]int **zippo 不是同一类型的对象。

就像int zippi[5]zippy是一块内存的地址。 但是编译器知道你想用二维语法寻址从zippy 开始的八个内存位置,但是想用一维语法寻址从zippi 开始的五个内存位置。

zippo 完全不同。 保存一块足够大的内存块的地址以包含两个指针,如果你让它们指向一些整数数组,你可以用二维解引用它们数组访问语法。

【讨论】:

【参考方案5】:

Reed 解释得很好,我再补充几点以使其更简单,当我们引用zippozippo[0]zippo[0][0] 时,我们仍然指的是数组@987654324 的相同基地址@。原因是数组总是连续的内存块,而多维数组是多个连续放置的单维数组。

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

【讨论】:

以上是关于C多维数组中的指针地址的主要内容,如果未能解决你的问题,请参考以下文章

多维数组与指针

C多级指针与多维数组

C之多维数组和多维指针(三十一)

C 语言数组 ( 多维数组本质 | n 维数组名称本质 是 n-1 级数组指针 )

《C专家编程》数组和指针并不同--多维数组

C中的多维数组:它们是锯齿状的吗?