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
、&zippo
、*zippo
、zippo[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
不是指针。它是一个数组值数组。 zippo
和 zippo[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 在这里为您进行自动转换)。
编辑:当我说数组“衰减”为指针时,我的意思是值上下文中数组的名称等价于指针。因此,如果我有某个类型 T
的 T pt[100];
,那么名称 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);
打印时,它们应该打印相同的值,但指针不同,它们分别指向整个矩阵、一行和一个整数。
【讨论】:
让我们看看我是否有这个权利:即使zippo
和zippo[0]
指向不同的内存量,它们的起始地址是相同的,并且在值上下文中它们都将指向 zippo 中的第一个整数。正确的?而且,这就是你所说的“衰变”吗?
没有。值上下文中的zippo[0]
是int *
,并指向第一个整数,其中值上下文中的zippo
是“指向int
的数组[2] 的指针”。你可以这样做:例如int (*p)[2]; p = zippo;
。然后,p[0] == 2
和 p[1] == 4
。对于“衰减”,请参阅我的(即将出现的)编辑。【参考方案4】:
这里重要的是int zippy[4][2]
与int **zippo
不是同一类型的对象。
就像int zippi[5]
,zippy
是一块内存的地址。 但是编译器知道你想用二维语法寻址从zippy
开始的八个内存位置,但是想用一维语法寻址从zippi
开始的五个内存位置。
zippo
完全不同。 它保存一块足够大的内存块的地址以包含两个指针,如果你让它们指向一些整数数组,你可以用二维解引用它们数组访问语法。
【讨论】:
【参考方案5】:Reed 解释得很好,我再补充几点以使其更简单,当我们引用zippo
或zippo[0]
或zippo[0][0]
时,我们仍然指的是数组@987654324 的相同基地址@。原因是数组总是连续的内存块,而多维数组是多个连续放置的单维数组。
当你必须按每一行递增时,你需要一个指针int *p = &zippo[0][0]
,而p++
每行递增指针。
在您的示例 id 中,它是一个 4 X 2 数组,在执行 p++
时,指针当前指向第二组 4 个元素。
【讨论】:
以上是关于C多维数组中的指针地址的主要内容,如果未能解决你的问题,请参考以下文章