【问题标题】:Why is address of an array variable the same as itself? [duplicate]为什么数组变量的地址与自身相同? [复制]
【发布时间】:2011-08-24 08:25:06
【问题描述】:

在 C 中,如果我们有一个像 a[10] 这样的数组,那么 a 和 &a 具有相同的指针值(但类型不同)。我想知道为什么 C 是这样设计的?

这是为了节省存储&a 所需的额外空间吗? ...当您想到 a 永远不能指向任何其他位置这一事实时,这是有道理的,因此存储 &a 是没有意义的。

【问题讨论】:

  • C 实际上已经对代码中 &a 的含义进行了多次迭代。至于 dmr 最初的意图 - 问他。
  • a 是一个地址。数组第0个元素的地址和数组的地址是一样的。这有帮助吗?
  • @Scott: a 不是地址。 a 是一个数组。数组变量可以隐式转换为其初始元素的地址,并且这种隐式转换在大多数情况下都会发生,但这并不意味着a 是一个指针。
  • @nbt:唉,这已经不可能了。 :-(

标签: c arrays pointers


【解决方案1】:

事实上,a[0] 与a 实际是同一个内存位置。 &a 表示存储a 的地址。

表示相同符号的方式不同。

转到数组的索引 3 (a[2]) 与 a + sizeof( typeof(a) ) * 3 相同,其中 typeof(a) 是变量的类型。

【讨论】:

  • 真的吗? &a 和a 一样,我还以为&a 是指针a 的存储地址
  • @Dan F:a 不是指针,它是一个数组。 &a 为您提供数组的地址。在大多数表达式中,数组名称a 被隐式转换(“衰减”)为指向其初始元素的指针,因此a 在这些表达式中实际上等效于&a[0]。 &a[0] 和 &a 指向同一个位置,但它们的类型不同(前者是 int* 类型,而后者是 int (*)[N] 类型,其中 N 是数组中元素的数量)。
  • 按照标准,我在int a[10] a 中对它的理解是一个指针,编译器将其视为指针,它只是在堆栈而不是堆上分配10个对象。而&a 将指向该堆栈分配变量的地址,而不是存储第一个元素的堆栈位置。数组不是 c/c++ 中的类型
【解决方案2】:

a 永远不能指向任何其他位置的事实

不过,这不是事实。如果a 是一个数组,则a 不会指向任何地方,因为a 不是指针。给定int a[42];,a 命名一个包含 42 个int 对象的数组;它不是指向 42 个int 对象数组的指针(即int (*a)[42];)。

&x 给你对象x 的地址;如果x 是一个数组类型变量,那么&x 给你数组的地址;如果不出意外,这与 & 对任何其他对象的行为一致。

更好的问题是“为什么在大多数情况下使用数组(如a)会衰减为指向其初始元素的指针?”虽然我不确定为什么该语言是这样设计的,但它确实使许多事情的规范变得更加简单,特别是,使用数组的算术实际上与使用指针的算术相同。

【讨论】:

  • 因为 a 不是 42 个整数的数组,而是一个指向最小大小为 42 * sizeof(int) 的内存位置的指针。 :) 数组是人类的概念。 C 没有它们。 a[21] 只是 a[0] + 21 * sizeof(int) 的地址。
  • @Mel:声明 int a[42]; 声明了一个数组。它没有声明任何指针。 C 确实有数组。
  • 我仍然投票你把它读成一个数组。真正发生的是创建了一个地址范围,其大小在编译时固定,并且指针被混淆了。因此 a[0] = 1 与 *a = 1 相同,a[1] 为 *(a+1)。但我也满足于将单词数组别名为一个隐藏指针,指向一个以上类型大小的内存块。 :)
  • @Mel 我推荐你阅读webcache.googleusercontent.com/…
  • 我怀疑arrays-decay-to-pointer设计的一个主要好处是它意味着一个以“数组”作为参数的函数实际上需要一个指针,它允许相同的函数对不同大小的数组进行操作。事后看来,允许数组作为一等公民传递可能会更好,并在幕后传递指针和大小。
【解决方案3】:

是的

您的解释是正确的,尽管我怀疑 amount 的空间是否是问题,而是需要分配它的特殊情况。通常,C 处理的每个对象都有一个值(或多个值)和一个地址。所以,一个实际分配的指针本身已经有一个地址,对于真正的指针来说,值和地址都可用是有意义的。

但是数组引用已经是一个地址。对于 C 来说,通过 & 运算符创建一个双间接指针需要在某处分配空间,这对于简单的早期 dmr C 编译器来说代表着哲学上的巨大差异。

这个新指针应该存储在哪里是一个很好的问题。与数组具有相同的存储类?如果它是一个参数呢?这是Pandora's box,解决它的最简单方法是定义操作。如果开发人员想要一个间接指针,他总是可以声明一个。

另外,& 返回数组对象的地址是有意义的,因为这与它在其他地方的使用是一致的。

查看此问题的一个好方法是查看对象具有值和地址,而数组引用只是一种简写语法。实际上要求&a 会有点迂腐,因为引用a 无论如何都不会有其他解释。

【讨论】:

    【解决方案4】:

    当您考虑在程序集级别如何引用数组时,该设计非常优雅且非常必要。使用 x86 汇编,考虑以下 C 代码:

    void f(int array[]) { return; }
    void g(int (*array)[]) { return; }
    
    int main()
    {
        int a[5];
    
        f(a);
        g(&a);
    
        return 0;
    }
    

    数组a 将占用堆栈上的 20 个字节,因为在大多数平台上 int 通常占用 4 个字节。当寄存器EBP 指向堆栈的激活记录的底部时,您将看到上面main() 函数的以下程序集:

    //subtract 20 bytes from the stack pointer register ESP for the array
    sub esp, 20
    
    //the array is now allocated on the stack
    
    //get the address of the start of the array, and move it into EAX register   
    lea eax, [ebp - 20]
    
    //push the address contained in EAX onto the stack for the call to f()
    //this is pretty much the only way that f() can refer to the array allocated
    //in the stack for main()
    push eax
    call f
    
    //clean-up the stack
    pop eax
    
    //get a pointer to the array of int's on the stack
    //(so the type is "int (*)[]")
    lea eax, [ebp - 20]
    
    //make the function call again using the stack for the function parameters
    push eax
    call g
    
    //...clean up the stack and return
    

    汇编命令LEA,或“加载有效地址”,根据其第二个操作数的表达式计算地址,并将其移动到第一个操作数指定的寄存器中。因此,每次我们调用该命令时,它就像是地址运算符的 C 等效项。您会注意到数组开始的地址(即[ebp - 20],或从位于注册器EBP 中的堆栈指针地址的基址减去20 个字节)总是传递给每个函数@987654329 @ 和 g。这几乎是可以在机器代码级别完成的唯一方法,以便在另一个函数中引用在一个函数的堆栈中分配的一块内存,而不必实际复制数组的内容。

    要点是数组不与指针相同,但同时,在赋值运算符右侧引用数组的唯一有效方法,或者将它传递给函数是通过引用传递它,这意味着按名称引用数组实际上在机器级别与获取指向数组的指针完全相同。因此,在机器代码级别,a、&a 甚至 &a[0] 在这些情况下会演变为同一组指令(在本例中为 lea eax, [ebp - 20]。但同样,数组类型是 not指针,和a,和&a不是同一个类型。但是由于它指定了一块内存,所以最简单有效的方法是通过指针来获取它的引用。

    【讨论】:

      【解决方案5】:

      B 是 C 的直接祖先。它是一种无类型语言,其语法

      tab[10];
      

      有或多或少的意思

      Word tab_[10];
      Word tab = (Word)&tab_;
      

      在 C.I.E.它保留了 10 个字的内存,并用内存区的地址初始化了变量。

      当 C 进化时,保持数组(顺便说一句,不仅是数组变量,任何数组值,您可以通过指针和多维数组看到它)在指向其第一个元素的指针中衰减这一事实被认为是有用的.

      Dennis Ritchie home page 中的B Manual 包含有关 C 和 Unix 的其他历史信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-02
        • 1970-01-01
        • 2021-04-01
        • 2017-10-20
        • 2022-11-23
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        相关资源
        最近更新 更多