以下代码提问:
#include <stdio.h>
int main(int argc,char *arg[]){
if (argc>2){
int m=atoi(arg[1]);
int n=atoi(arg[2]);
int a[m][n];
int (*p)[m][n]=&a;
printf("p : %p, *p : %p, **p : %p\n",p,*p,**p);
}
return 0;
}
主要环境:gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)x86-64
gcc main.c
./a.out 2 4
输出:
p : 0xbfea7ef0, *p : 0xbfea7ef0, **p : 0xbfea7ef0
问题是p == *p == **p
的原因。我想这可能是因为a
是一个数组,一种常量指针,地址是特定的,这涉及gcc的一些实现细节。
答案 0 :(得分:6)
p
是指向尺寸为[m][n]
的数组的指针。该指针的值是a
的地址,因此打印p
会获得a
的地址。
*p
是一个维度为[m][n]
的数组。作为指针的“值”是指向数组的第一个元素的指针,即a[0]
。这与a
的地址相同。
**p
是一个维度为[n]
的数组。此指针的值是指向数组的第一个元素的指针,即a[0][0]
。这与a
的地址相同。
答案 1 :(得分:1)
对于固定大小的数组和可变修改的数组,观察到的行为是相同的:
#include <stdio.h>
int main(void)
{
enum { m = 3, n = 4 };
int a[m][n];
int (*p)[m][n] = &a;
printf("p : %p, *p : %p, **p : %p\n", p, *p, **p);
return(0);
}
在我的机器上,这产生了:
p : 0x7fff6c542520, *p : 0x7fff6c542520, **p : 0x7fff6c542520
当然,p
是两个程序中指向2D数组的指针(我不会再次添加'两个程序'限定符,即使它适用)。当您打印p
时,您将获得分配给它的数组的地址,即a
的地址。因为p
是指向2D数组的指针,*p
'是'2D数组,但在大多数情况下,数组引用成为指向其第一个元素的指针,因此*p
是指针到a[0]
,与a
引用的内存位置相同。类似地,**p
'是'1D数组,但类似地,**p
是指向a[0][0]
的指针,它也是与a
引用相同的内存位置。因此,这三个值应该是相同的,编译器才能正确。
这不容易阅读,但是,它正试图解释的C也不是。
以下是原始程序的一个小变化,它说明了p
,*p
和**p
所指向的不同对象的大小:
#include <stdio.h>
int main(void)
{
enum { m = 3, n = 4 };
int a[m][n];
int (*p)[m][n]=&a;
printf("p+0 : %p, (*p)+0 : %p, (**p) + 0 : %p\n",
(void *)(p+0), (void *)((*p)+0), (void *)((**p)+0));
printf("p+1 : %p, (*p)+1 : %p, (**p) + 1 : %p\n",
(void *)(p+1), (void *)((*p)+1), (void *)((**p)+1));
return(0);
}
严格来说,%p
转换规范应该是void *
;这里的演员强制执行。官方的原始代码有点草率,尽管很少有机器可以解决这个问题。
这个输出是:
p+0 : 0x7fff63453520, (*p)+0 : 0x7fff63453520, (**p) + 0 : 0x7fff63453520
p+1 : 0x7fff63453550, (*p)+1 : 0x7fff63453530, (**p) + 1 : 0x7fff63453524
注意指向的对象大小是如何不同的,如+1
版本所示:
sizeof(*p) = 0x30
sizeof(**p) = 0x10
sizeof(***p) = 0x04