【问题标题】:Address of array - difference between having an ampersand and no ampersand数组地址 - 有 & 号和没有 & 号之间的区别
【发布时间】:2011-10-09 23:07:38
【问题描述】:

我有一个看起来像这样的结构:

struct packet {
  int a;
  char data[500];
};
typedef struct packet packet_t;

我有点困惑,为什么下面的代码会为每个 printf 输出相同的地址:

void myfunction() {
  packet_t packet;
  printf("%p\n", packet.data);   //e.g., outputs 0x7fff1c323c9c
  printf("%p\n", &packet.data);  //e.g., outputs 0x7fff1c323c9c
}

有人对此有很好的解释吗?

【问题讨论】:

  • 您的意思是“为什么这两行给出相同的结果”,还是“为什么每个函数调用的结果都相同”?前者很容易(因为指针指向同一个东西),后者是不可能的。
  • 如果按名称引用数组而不使用方括号,它将伪装成指向第一个元素的指针。

标签: c


【解决方案1】:

在大多数情况下,“N-element array of T”类型的表达式将被转换为“指向T”类型的表达式,其值将是第一个元素的地址数组。这就是第一个printf 调用中发生的情况;具有char [500] 类型的表达式packet.data 被替换为char * 类型的表达式,它的值是第一个元素的地址,因此您实际上是在打印&packet.data[0]

当数组表达式是一元& 运算符的操作数时,会出现此规则的一个例外情况;表达式&packet.data 的类型是char (*)[500](指向char 的500 元素数组的指针)。

数组的地址与第一个元素的地址相同,因此对printf 的两次调用都显示相同的;只是表达式的类型不同。为了迂腐,两个表达式都应该在 printf 调用中转换为 void *%p 转换说明符需要一个 void * 参数):

printf("%p\n", (void *) packet.data);
printf("%p\n", (void *) &packet.data);

【讨论】:

    【解决方案2】:

    这是因为数组衰减到指向序列中第一个元素的指针。所以,packet.data 地址位置与&packet.data&packet.data[0] 相同。

    【讨论】:

    • 虽然这两种类型不同:前者是指向数组的指针,后者是指向字符的指针。两者恰好位于同一个数字地址。
    【解决方案3】:

    根据 C11 (6.3.2.1.3) 部分。 当数组用作sizeof 和一元& 运算符的操作数时,数组将衰减为指向其第一个元素except 的指针。

    因此packet.data 不会在表达式&packet.data 中衰减为&packet.data[0]。结果将是一个char (*)[500] 类型的指针,它在数值上与其第一个元素的地址相同,即packet.data[0]

    【讨论】:

      【解决方案4】:

      我不知道为什么这被否决了,这是一个暴露 C 令人困惑的行为的好问题。

      混淆是因为通常在定义数组时会创建一个真正的指针:

      char data[100];
      printf("%p\n", data);    // print a pointer to the first element of data[]
      printf("%p\n", &data);   // print a pointer to a pointer to the first element of data[]
      

      因此,在典型的 32 位桌面系统上,为 data 分配了 4 个字节,这是一个指向 100 个字符的指针。 Data,指针,本身就存在于内存中。

      在结构中创建数组时,不会分配指针。相反,编译器在运行时将对packet.data 的引用转换为指针,但不分配任何内存来存储它。相反,它只使用&packet + offsetof(data)

      就我个人而言,我希望语法保持一致并且需要一个 & 符号,packet.data 会产生某种编译时错误。

      【讨论】:

      • 这个答案可能不正确。我尝试运行代码(gcc 4.8),它的行为与 C 和 C++ 中的 OP 代码完全相同;即 "&arr" 与 &(arr[0]) 相同,无论数组是单独声明还是作为结构的成员声明。
      • @EmilyChen 是对的。具体来说,这个答案中的代码 sn-p 与问题做同样的事情但没有结构仍然会打印相同的地址两次。因此,就结构与问题的关系而言,结构如何存储数组似乎没有什么特别之处。 &data where data is not in a struct 不是指向第一个元素的指针的指针,它只是指向第一个元素的指针,并且当两者都转换为 (void*) 时与 data 相同.
      • 当您只调用printf("%p", data);data 是一个数组,根本不会在内存中分配指针data 的第一个元素的地址保存在寄存器中。当您没有定义(指针)变量来存储地址时,编译器不需要将地址保存在内存中。 (即使关闭优化,您也可以看到此行为)。
      • 这个答案完全错误:John Bode 下面的答案是正确答案,tstanisl 的答案引用了 C11 标准的相关部分。
      【解决方案5】:

      因为除了让 &packet.data 导致编译错误之外,这是唯一合理的做法。整数 a 和数据 char 数组在堆栈中按顺序排列,不涉及解引用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-19
        相关资源
        最近更新 更多