参数值传递的汇编实质,就是在堆栈上创建存储区,看看以下实现:
先来看看Delphi代码,传一个数组参数进去
1 procedure SortIt(a: array of Integer); 2 var 3 i, j: Integer; 4 begin 5 j := Length(a); 6 for i:=1 to j do 7 a[i-1] := i; 8 end;
调用代码为:
procedure TForm1.btn1Click(Sender: TObject); var a: array[0..1] of Integer; begin SortIt(a); ShowMessage('a[0]: ' + IntToStr(a[0]) + #13#10 + 'a[1]: ' + IntToStr(a[1])); end;
看看汇编代码是怎样干的:
Unit1.pas.44: SortIt(a); ;调用前准备 lea eax,[ebp-$08] ;定义的局部变量放在堆栈里,2个数组元素,32位系统里占8字节, ;根据定义的局部变量顺序压栈,这儿只定义了数组A,所以堆栈里 ;就只有8字节,此句的意思是,把ebp-8的值放到eax中,这个值是 ;指向a[1]的内存地址,所以执行后eax中为a[0]元素的内存地址 mov edx,$00000001 call SortIt
函数入口:
1 ;函数入口处代码 2 00452688 55 push ebp ;这两行为堆栈框架,函数开始前必这样做以保存调用前 3 00452689 8BEC mov ebp,esp ;的堆栈数据,同时为同时为函数创造干净的堆栈环境 4 0045268B 53 push ebx ;要用到ebp,先保存原值,不要破坏它 5 0045268C 8BCA mov ecx,edx ;edx值为1,看起来用于下面的循环,ecx一般用于计数 6 0045268E 85C9 test ecx,ecx ;test和and类似,不同的是,只影响标志寄存器,不影响结果 7 00452690 7807 js +$07 ;如果sf为1,这是符号位,如果ecx为ffff ffff,数组就爆了,不能是大于这个值 8 00452692 8B1C88 mov ebx,[eax+ecx*4] ; 记得eax为a[0]吧,此处ecx为1, eax+4刚好为a[1]地址,把a[1]的值取到ebx 9 00452695 49 dec ecx 10 00452696 53 push ebx ; 把a[1]值入栈 11 00452697 79F9 jns -$07 ;返回上面的mov ebx,[eax+ecx*4],再来一次,这样的话又能把a[0]值入栈 12 00452699 8BC4 mov eax,esp ;这个时候堆栈里从低地址到高地址已形成a[0]、a[1]的结构,把堆栈指针指向a[0], 13 ;一个新的临时参数环境就构造好了,后面对数组的存取就只在这儿进行,不会影响 14 ;原值,高明呀,原来是这样实现的!
函数退出时:
1 ;函数结束堆栈要恢复到入来前的状态,这叫堆栈平衡 2 004526B5 8BE5 mov esp,ebp ;记得函数进来前ebp指向栈顶吧,这一下直接回到进来前状态 3 004526B7 5D pop ebp ;是push ebp和mov ebp,esp的倒过来,就恢复了。 4 004526B8 C3 ret