为什么数组变量的地址与它自身相同?

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

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

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

但这不是事实。 如果a是数组,则a不指向任何位置,因为a不是指针。 给出int a[42];a名为42个int对象的数组; 它不是一个指向42个int对象数组的指针(即int (*a)[42]; )。

&x为您提供对象x的地址; 如果x是数组类型变量,则&x为您提供数组的地址; 如果不出意外,这与&对任何其他对象的行为一致。

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

当您考虑如何在assembly级别引用arrays时,设计非常优雅且非常必要。 使用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] ,或者从位于reigister EBP中的堆栈指针地址的基数中减去20个字节)是总是传递给每个函数fg 。 这是在机器代码级别完成的唯一方法,以便在另一个函数中引用一个函数的堆栈中分配的一块内存,而无需实际复制数组的内容。

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

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

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

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

你的解释是在正确的轨道上,虽然我怀疑空间是否是问题,而是需要分配它的特殊情况。 通常,C处理的每个对象都有一个值(或值)和一个地址。 因此,实际分配的指针本身已经有一个地址,并且对于真正的指针而言,值和地址都可用是有意义的。

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

存储这个新指针的位置是一个很好的问题。 使用与arrays相同的存储类? 如果它是一个参数怎么办? 它是Pandora的盒子 ,解决它的最简单方法是定义操作。 如果开发人员想要一个间接指针,他总是可以声明一个。

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

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

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

 tab[10]; 

或多或少的含义

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

在CIE中,它保留了10个字的内存,并用内存区的地址初始化变量。

当C演化时,认为数组(BTW不仅是一个数组变量,任何数组值,你可以用指针和多维数组看到它)在指向其第一个元素的指针中衰减这一事实被认为是有用的。

B Dennis Ritchie主页上的 手册 ,其中包含有关C和Unix的其他历史信息。