【问题标题】:in C, how come the address of array, &array, &array[0] are the same?在C语言中,为什么array、&array、&array[0]的地址是一样的?
【发布时间】:2021-05-04 06:35:24
【问题描述】:
int main(){
        long a[]={6,9,10};

        printf("%p\n",a);
        printf("%p\n",&a);
        printf("%p\n",&a[0]);

return 0;
}

结果是:

0x7ff9c94a10
0x7ff9c94a10
0x7ff9c94a10

这个结果真的让我很困惑! 为什么它在同一个内存位置存储不同的值,在 0x7ff9c94a10 存在地址本身和值 6 同时存在

【问题讨论】:

标签: arrays c compilation


【解决方案1】:

图片应该有帮助 - 我们假设是 64 位 long

             +––––––+
0x7ff9c94a10 | 0x06 | a[0]
             +––––––+
0x7ff9c94a18 | 0x09 | a[1]
             +–––—––+
0x7ff9c94a20 | 0x0a | a[2]
             +–––––—+

没有对象a与数组元素本身分开,所以数组的地址(&a,类型long (*)[3])与其第一个元素的地址相同(&a[0],类型@ 987654327@).

除非它是sizeof 或一元& 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则表达式 类型为“N- T" 的元素数组将被转换或 "decay" 为 "pointer to T" 类型的表达式,表达式的值是数组第一个元素的地址,所以表达式 @987654332 @ 实际上与 &a[0] 相同,并且也具有 long * 类型。

【讨论】:

    【解决方案2】:

    a&a[0]同义词(根据定义),正如 K&R 在“数组和指针”一章中轻轻解释的那样。 &a 没有提到,但可以视为中间步骤。

    您必须专注于标识符,这里是a。编译器所需要的只是a 开始的地址及其元素的类型。第二个元素必须在基址加上一个元素大小。

    &a&a[0] 是要计算的表达式,而不是要存储的值。 &a[23] 也是计算的,而不是存储的:a + 23 * elemsize。

        printf("%p\n",   a     );  
    
        printf("%p\n",   a+1   );  // address of next element, not byte  
        printf("%p\n", &(a[1]) );  // parens not needed
    

    第 2 行和第 3 行在数组开始上方打印 8 个字节(长大小)的地址。


    在 0x7ff9c94a10 存在地址本身

    也许那个地址确实存在于同一个地方。就像我们都以某种方式做的那样。但它有一个很长的“6”。

    要使用地址填充数组,您可以:

        void *a[3];
        for (int i = 0; i < 3; i++)
            a[i] = &a[i];
    
    
        for (int i = 0; i < 3; i++) {
            printf("%p\n",  a[i]);
            printf("%p\n", &a[i]);
        }
    

    打印:

    0x7ffe0918cbe0
    0x7ffe0918cbe0
    0x7ffe0918cbe8
    0x7ffe0918cbe8
    0x7ffe0918cbf0
    0x7ffe0918cbf0
    

    存储一个地址/指针需要 8 个字节。 longvoid * 在这里大小相同。


    printf("%p\n", a+i); 也可以。无需引用取消引用的元素:&amp;a[i]

    一个基本的数组和指针规则是E1[E2] is *(E1 + E2)。 E1 必须指定一个数组,而 E2 和整数,所以

    a[i]
    *(a + i)
    

    都是一样的。一样

    &(a[i])
    &a[i]
    a + 1
    

    从定义的地址开始的数组的想法很简单。但是说起来……自然语言可以有双重意义:

    "Have you seen number 12 today?" (the guest)
    
    "Did you clean number 12?" (not the guest, the room)
    

    困惑

    不知何故。但是在void **b = a; 之后,您可以像a 一样使用指针b(只是它有自己的新&amp;ba = ... 是不可能的(分配给裸数组)。

    新示例

    您的long 类型非常适合真正存储指向某物的指针,但它使上面的示例变得复杂。这里是一个简单的 int 数组,所以没有人会想要存储地址。它显示了数组和指针共享的内容和不共享的内容:

    int main(){
            int a[3] = {10,20,30};
    
            int *b;
            b = a;
    
            for (int i = 0; i < 3; i++) {
                printf("%p\n",  &a[i]);
                printf("%p\n",  &b[i]);
                printf("%p %d\n",   a+i, a[i]);
                printf("%p %d\n",   b+i, b[i]);
            }
            printf("%p\n",        a);
            printf("%p b = a\n",  b);
            printf("%p\n",                          &a);
            printf("%p <-- different, &b != &a\n",  &b);
    
    return 0;
    }
    

    这给出了:

    0x7fff72f0ed8c
    0x7fff72f0ed8c
    0x7fff72f0ed8c 10
    0x7fff72f0ed8c 10
    0x7fff72f0ed90
    0x7fff72f0ed90
    0x7fff72f0ed90 20
    0x7fff72f0ed90 20
    0x7fff72f0ed94
    0x7fff72f0ed94
    0x7fff72f0ed94 30
    0x7fff72f0ed94 30
    0x7fff72f0ed8c
    0x7fff72f0ed8c b = a   (same as top index zero)
    0x7fff72f0ed8c
    0x7fff72f0ed80 <-- different, &b != &a
    

    差异(12 个字节,“0”代表“c”)并不显着,但这并不重要。 b 除了数组元素还有生命,a 没有。

    除此之外,指针b 可以被分配(就像b = a 一样),但数组a 不能。

    怎么会?

    数组的地址、&array、&array[0]

    这其实很不清楚。 array 的地址是 &array(不知何故),但 &amp;array 的地址既不是 &&amp;array(关于标签“数组”的错误)也不是 &(&amp;array)(错误:需要左值) .

    【讨论】:

      【解决方案3】:

      嗯,

      array
      

      根据定义,是第一个元素的地址,与:

      &array[0]
      

      所以这就解释了为什么两者有相同的地址。

      第二个表达式是数组的地址。通常,(我现在不记得这是否由标准强制执行)一个数组是通过将其元素一个接一个地放置来存储的,从第一个开始......所以数组地址是正常的与第一个元素的地址相同。但是如果你检查指向对象的类型,你会发现这些表达式之间的区别:

      *array
      

      是数组元素的类型,而

      *&array
      

      是整个数组类型。

      所以,假设你有

      char array[10];
      

      然后

      array
      

      属于类型

      char *
      

      *array
      

      类型为char,而

      &array
      

      是 10 个字符的数组类型,或者

      char [10]
      

      如何验证?只需运行以下程序:

      #include <stdio.h>
      
      /* print the expression and it's size */
      #define P(T) printf("sizeof("#T") == %zu\n", sizeof(T))
      
      int main()
      {
          char array[10];
      
          P(   array    );
          P(  *array    );
          P(  &array    );
          P( *&array    );
          P(   array[0] );
          P(  &array[0] );
          P( *&array[0] );
      }
      

      当(在我的系统上)运行时,它会产生:

      $ a.out
      sizeof(array) == 10         <-- the size of the array
      sizeof(*array) == 1         <-- the size of the pointed to first element (as by def)
      sizeof(&array) == 8         <-- size of a pointer to the array.
      sizeof(*&array) == 10       <-- size of the pointed to object (the array)
      sizeof(array[0]) == 1       <-- size of the first element.
      sizeof(&array[0]) == 8      <-- size of a pointer to the first element (as above)
      sizeof(*&array[0]) == 1     <-- size of the pointed to first element.
      $ _
      

      (右边的文字是对数字的解释)

      &amp;&amp; 上错误的原因(这属于给出的答案之一,由于某种原因将&amp; 链接到它的两个应用程序中并得到一个关于标签运算符的错误)是 GCC 有一元运算符&amp;&amp; 用于语言的 GCC 扩展。 C 中没有&amp;&amp; 一元运算符,如果您继续使用-pedantic,您会收到一个错误,即在尝试使用没有左操作数的逻辑与运算符(&amp;&amp;)时出现语法错误。无论如何,如果您尝试像中那样分隔操作数

      & & array
      

      然后你会得到另一个错误,因为子表达式 &amp;array 不表示左值(在内存中没有表示),因此不能应用 &amp; 左一元运算符(你不能得到一个值的地址)。

      你的最后一段很有趣:你说

      这个结果真的让我很困惑!为什么它在同一个内存位置存储不同的值,在 0x7ff9c94a10 存在地址本身和值 6 同时存在

      它们不是不同的值。这里的问题是,三个不同的表达式中的两个表示不同的东西,但在同一个地方,因为数组在内存中的第一个元素开始于同一点。

      当你在家时,你和你家的邮政地址相同,但你和你家是不同的东西,对吧?这正是这里发生的事情:&amp;array(是您的房子的地址,数组)和&amp;array[0](是您的地址,与房子的邮政地址相同,因为您在房子里)。问题是你有三个表达式并且不明白表达式 array 的含义,根据它是如何定义的,字面意思是 指向第一个元素的指针 而这个使其等同于&amp;array[0](如缩写)。所以我们最终得到了三个具有相同值的表达式,其中两个是相同的东西(确切地说),另一个意味着不同的东西。大概读完这一段之后,你需要再看一遍上面这个简单程序的输出,你会发现不同之处在哪里(很明显,你的房子比你大)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        • 1970-01-01
        • 1970-01-01
        • 2018-02-13
        • 2019-03-12
        相关资源
        最近更新 更多