3

来自以下代码的问题:

#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 的一些实现细节。

4

2 回答 2

6

p是指向具有维度的数组的指针[m][n]。该指针的值是 的地址a,因此打印p会得到 的地址a

*p是一个维度为 的数组[m][n]。this 作为指针的“值”是指向数组第一个元素的指针,即a[0]. 这是与 相同的地址a

**p是一个维度为 的数组[n]。此指针的值是指向数组第一个元素的指针,即a[0][0]. 这又是同一个地址a

于 2013-04-09T02:47:50.733 回答
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是指向两个程序中的二维数组的指针(我不会再次添加“在两个程序中”限定符,即使它适用)。当您打印时p,您将获得分配给它的数组的地址,即a. 因为pis 指向 2D 数组的指针,所以*p'is' 2D 数组,但在大多数情况下,数组引用变成指向其第一个元素*p的指针,指向 的指针也是如此a[0],它与引用位于相同的内存位置a。类似地,**p“是”一维数组,但类似地,**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
于 2013-04-09T02:28:40.493 回答