int foo(int bar,int* baz)
{
char snink[4];
short* why;
}
栈段示意图:
注意:
1.局部变量参数根据声明从右至左而在栈中从高地址至低地址存放,即,第0个参数(bar)总是放在其他参数的下方,第1个参数在第0个参数之上。空间位域参数位于局部变量下方(也就是位于低地址处)。在空间位域参数和局部变量之间存储着调用函数的某些信息,以便告诉我们到底是哪块代码调用了这个函数(foo)。
函数调用:
int main(int argc, char** argv)
{
int i=4;
foo(i,&i);
return 0;
}
对应的汇编代码:
SP=SP-4;
M[sp]=4;
SP=SP-8;
R1=M[SP+8];
R2=SP+8;
M[SP]=R1;//i
M[SP+4]=R2;//&i
CALL<foo>;//跳转指令
SP=SP+8;//这个值就是saved pc
栈段示意图:
注意:
1.SP(stack pointer)是一个特定的寄存器,这个寄存器总是指向执行中的栈的最低地址,因此当main调用时,sp指向saved PC。
2.每条汇编语句都默认为4字节。
补充完整的foo函数:
int foo(int bar,int* baz)
{
char snink[4];
short* why;
why=(short *)(snink+2);
*why=50;
return 0;
}
对应的汇编代码:
<foo>
//为局部变量留空间
SP=SP-8
//没有初始化
R1=SP+6;
M[SP]=R1;//将snink[2]的地址读入寄存器中
R1=M[SP];
M[R1]=.2 50;
SP=SP+8//SP指向返回地址
RET;//return这条指令把SP的值取出来放到PC,同时SP+=4
一般栈段内的活动记录示意图:
这个活动记录使得函数能够执行并且让它可以访问所有的参数和局部变量。
活动纪律被分为用户调用函数部分和被调用函数部分这两部分的原因:因为调用者并不知道上面例子中的foo函数在实现过程中有多少局部变量。
int fact(int n)
{
if(n==0)
return 1;
return n*fact(n-1);
}
对应的汇编代码:
<fact>
R1=M[SP+4];
BNE R1,0,PC+12//若条件不成立,跳出if语句,也就是三行汇编代码(3*4)
RV=1;
RET;
R1=M[SP+4];
R1=R1-1;
SP=SP-4;
M[SP]=R1;
CALL<fact>;
SP=SP+4;//回收局部变量空间
R1=M[SP+4]
RV*R1;
RV;
注意:
在汇编语言中,处理PC,SP,还有一个寄存器叫做RV,RV寄存器专门用来在调用者和被调用函数之间传递返回值。