《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,如是而已——闲着没事,乱谈一番。