【问题标题】:Why in a 2D array a and *a point to same address? [duplicate]为什么在二维数组中 a 和 *a 指向同一个地址? [复制]
【发布时间】:2018-01-12 11:56:26
【问题描述】:

我只是想了解如何实现二维数组以及如何进行内存分配。所以我对给定的 c 程序有些疑问,为什么 a 和 *a 给出相同的地址。

#include<stdio.h>
main()
{
    int i,j;
    int a[3][3]={1,2,3,4,5,6,7,8,9};


    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
        {
            printf("%d\t",*(*(a+i)+j));
        }
        printf("\n");
    }


    printf("%d\n",a);
    printf("%d\n",a[0]+1);
    printf("%d\n",a[0][0]);
    printf("%d\n",a+1);
    printf("%d\n",*a);
}

这是输出

【问题讨论】:

标签: c arrays multidimensional-array


【解决方案1】:

2D 数组与第一个 1D 数组位于同一地址,而第一个 1D 数组又与第一个元素位于同一地址。就是这样,没有别的了。

【讨论】:

  • “开始于”比“驻留在”更好。该数组驻留在许多地址上,其中许多地址与其第一个子数组或该子数组的第一个元素不同。
  • @Eric 虽然这是真的,但对于int 来说也是如此,它仍然可以说是“存在”或“居住”在给定地址。除了字符和可能的布尔值之外的所有对象都大于单个可寻址实体。
【解决方案2】:

理解二维数组的直观方式是这样的......

a --> a[0] --> [1,2,3]
      a[1] --> [4,5,6]
      a[2] --> [7,8,9]

a[0], a[1], a[2] 是逻辑表示,不是实际内存。

so a[0] = a + 0*[bytes occupied by one row], 
   a[1] = a + 1*[bytes occupied by one row], 
   a[2] = a + 2*[bytes occupied by one row] 

因此*a =&gt; a[0] =&gt; a

请注意,即使地址相同,当我们调用 aa[0]*a 时,指针的“类型”也会发生变化。这可以通过 (a+1) 和 (a[0]+1) 的打印输出轻松看出。

【讨论】:

    【解决方案3】:

    事实上,数组是内存的范围。

    在表达式中,数组指示符被转换为指向它们的第一个元素的指针。

    具有int (*)[3][3] 类型的表达式&amp;a 给出了与数组a 存在的相同范围的地址。这种情况下数组a占用的内存可以这样解释

    int b[1][3][3]= { { {1,2,3 } , { 4,5,6 }, { 7,8,9 } } };
    

    并且表达式中使用的数组指示符b 被转换为地址到它的int ( * )[3][3] 类型的第一个元素,即&amp;a

    因此表达式

    &a[0][0]
    a[0]
    a
    &a
    

    产生与数组占用的范围地址相同的值。

    考虑到要输出指针,您应该使用转换说明符p 而不是d

    【讨论】:

    • 读了几遍我明白你想说什么,但是在 C 中两个变量不能引用同一个对象(例如,数组a 不能与数组在同一位置b)。您可以更清楚地说,3 维数组 b 可以看作是由 2 维数组组成的 1 维数组,每个数组都具有与 a 相同的 type
    • @PeterA.Schneider 我的英语不好,所以有时很难理解我想说的话。:)
    【解决方案4】:

    首先,您不应该使用%d 来打印指针的地址,而是使用%p%d 用于有符号整数,可能适合或不适合表示地址,具体取决于实现。接下来,您将在来自 C 标记 wiki 的其他 answer 中找到有趣的参考。

    现在直接回答。 int a[3][3]; 声明了一个由三个整数组成的 3 个数组组成的数组。所以a[0](或*a)是第一个包含三个元素的数组。

    printf("%p\n", *a); 中,数组*a decays 指向一个指向它的第一个元素的指针,即&amp;a[0][0],换句话说,你得到了数组开头的地址。

    printf("%p\n", a); 中,数组a 也衰减为指向其第一个元素&amp;a[0] 的指针。这里又得到了数组开头的地址,这就是打印值相同的原因。

    但是即使它们指向同一个地址,&amp;a[0]&amp;a[0][0] 也是指向不同类型的指针:first 指向一个由 3 个整数组成的数组,而 second 指向一个整数。同样,&amp;a 仍将指向相同的地址,但仍指向另一种类型和由 3 个数组到 3 个整数组成的数组。

    由于对象的地址是其第一个字节的地址,(char *) a(char *) *a(char *) &amp;a 都是相等的,并且是指向数组第一个字节的 char 指针。这是合法的,因为 C 语言规定可以将指向对象的指针转换为指向指向对象第一个字节的 char 的指针。但是不允许将 a 传递给期望 int * 的函数,如果这样做,正确的编译器应该针对不同的间接级别发出警告。

    【讨论】:

    • Re “在printf("%p\n", *a); 中,数组*a 衰减为指向其第一个元素的指针,……”:在*a 中,数组自动转换为其第一个元素的地址元素,不只是一个。很可能您对 C 非常熟悉,以至于您自动将*a 视为代表a 的第一个子数组。你不需要有意识地完成它。但是,如果我们要回答那些对此不熟悉并且必须明确地通过这些转换进行推理的人,那么跳过一个步骤可能看起来很神秘。
    • @EricPostpischil:你在想*aa[0]吗?
    • printf("%p\n", *a) 中,a 是一个左值,它是一个数组。它被转换为指向其第一个元素的指针。然后* 取消引用指针,结果是一个左值,它是一个数组。它被转换为指向其第一个元素的指针。
    • @EricPostpischil 我明白你的意思!但老实说,我认为对于初学者来说,认为a[i] 是数组a 的第i 个元素比将a 转换为指向其第一个元素的指针,然后进行指针运算得到的左值更简单获取指向第 i 个元素的指针并最终取消引用它...
    猜你喜欢
    • 1970-01-01
    • 2015-08-16
    • 2016-12-14
    • 2019-07-15
    • 2019-10-18
    • 2019-01-24
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多