设计相当优雅,考虑到在汇编层面如何引用数组,这是非常必要的。使用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()
函数的以下汇编代码:
sub esp, 20
lea eax, [ebp - 20]
push eax
call f
pop eax
lea eax, [ebp - 20]
push eax
call g
汇编指令
LEA
,或者“加载有效地址”,计算第二个操作数的表达式的地址并将其移动到由第一个操作数指定的寄存器中。因此,每次调用该命令时,它就像C语言中的取地址运算符。您会注意到,数组开始的地址(即
[ebp-20]
,或从寄存器
EBP
中的堆栈指针地址的基础上减去20个字节)始终传递给每个函数
f
和
g
。这是在机器码级别上唯一能够在不必复制数组内容的情况下引用在一个函数的堆栈中分配的内存块的另一个函数的方法。
结论是数组与指针不同,但是在右侧赋值运算符或传递给函数中引用数组的唯一有效方法是通过引用传递它,这意味着按名称引用数组在机器级别上实际上与获取数组指针相同。因此,在机器码级别上,这些情况下的
a
,
&a
甚至
&a[0]
都会转化为相同的一组指令(在本例中为
lea eax,[ebp-20]
)。但是请注意,数组类型不是指针,
a
和
&a
也不是相同类型。但是,由于它指定了一块内存,因此获得对其的引用最简单有效的方法是通过指针。
a
不是一个地址,而是一个数组。数组变量可以隐式转换为其初始元素的地址,并且在大多数情况下会发生这种隐式转换,但这并不意味着a
是一个指针。 - James McNellis