《C专家编程》一书中的216页有一个很有意思的示例。它形象的展示了数组形参在函数内部是如何作为指针来使用的。
为了简单,稍微简化了一下:
代码
#include <stdio.h>
int ga[10];
void array_fun(int a[])
{
a[0] = 1;
printf("&a = %#x\n", &a);
printf("&a[0] = %#x\n", &a[0]);
}
int main()
{
printf("&ga = %#x\n", &ga);
printf("&ga[0] = %#x\n", &ga[0]);
array_fun(ga);
return 0;
}
int ga[10];
void array_fun(int a[])
{
a[0] = 1;
printf("&a = %#x\n", &a);
printf("&a[0] = %#x\n", &a[0]);
}
int main()
{
printf("&ga = %#x\n", &ga);
printf("&ga[0] = %#x\n", &ga[0]);
array_fun(ga);
return 0;
}
输出结果如下:
&ga = 0x4394b0
&ga[0] = 0x4394b0
&a = 0x12ff30
&a[0] = 0x4394b0
为了更好的理解数组形参,下面给出汇编代码(VC 6.0下):
代码
7: a[0] = 1;
00402878 mov eax,dword ptr [ebp+8]
0040287B mov dword ptr [eax],1
8: printf("&a = %#x\n", &a);
00402881 lea ecx,[ebp+8] #实参在栈中的位置(实参本身的内存地址)-->ecx
00402884 push ecx
00402885 push offset string "&a = %#x\n" (00432038)
0040288A call printf (00404910)
0040288F add esp,8
9: printf("&a[0] = %#x\n", &a[0]);
00402892 mov edx,dword ptr [ebp+8] #实参的内容(数组的地址) -->edx,(注意实参本身的地址(通过lea指令获取)与数组地址的差别)
00402895 push edx
00402896 push offset string "&a[0] = %#x\n" (00432028)
0040289B call printf (00404910)
004028A0 add esp,8
00402878 mov eax,dword ptr [ebp+8]
0040287B mov dword ptr [eax],1
8: printf("&a = %#x\n", &a);
00402881 lea ecx,[ebp+8] #实参在栈中的位置(实参本身的内存地址)-->ecx
00402884 push ecx
00402885 push offset string "&a = %#x\n" (00432038)
0040288A call printf (00404910)
0040288F add esp,8
9: printf("&a[0] = %#x\n", &a[0]);
00402892 mov edx,dword ptr [ebp+8] #实参的内容(数组的地址) -->edx,(注意实参本身的地址(通过lea指令获取)与数组地址的差别)
00402895 push edx
00402896 push offset string "&a[0] = %#x\n" (00432028)
0040289B call printf (00404910)
004028A0 add esp,8
结论:
对数组形参本身取地址,即 &a,则相当于LEA R, [ebp+8] (R表示寄存器),本质上相当于求参数本身的地址;
而对数组形参的元素取地址,即 &a[n],则相当于MOV R,[ebp+8+n*scale],尽管,这是C的语法,但是不能不看到LEA与MOV的精妙与区别所在。
指针(*),则相当于寄存器间接寻址的MOV;取址(&),则相当于寄存器间接寻址的LEA,如是而已——闲着没事,乱谈一番。