【问题标题】:address of pointer to C multi-dimension array指向 C 多维数组的指针的地址
【发布时间】:2013-03-31 07:20:24
【问题描述】:

来自以下代码的问题:

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

【问题讨论】:

  • 否;这不是 GCC 的怪癖;这是预期和要求的行为。不过,解释它比仅仅陈述更难!
  • 尝试描绘通过组织范式访问的内存块,该组织范式由您使用的类型化语言决定。现在,转到该块的开头;三次。是的。它仍然与之前的时间(和之前的时间)相同。如果它有帮助,请考虑a 数组在内存中的位置。现在是&amp;a&amp;a[0]&amp;a[0][0]。你认为其中任何一个会不同吗?
  • C 中的数组很奇怪。这是最简单的解释。
  • 是的@WhozCraig,在gcc下&a和a的值是一样的。

标签: c arrays pointers dereference


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    对于固定大小的数组和可变修改的数组,观察到的行为是相同的:

    #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 的地址。因为p是一个指向二维数组的指针,*p'是'二维数组,但是在大多数情况下,数组引用变成了指向它的第一个元素的指针,所以*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
    

    【讨论】:

      猜你喜欢
      • 2011-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多