c语言局部变量压栈顺序,C 程序局部变量压栈出栈的理解

写这篇总结的缘由仅仅出于巧合,五一前帮一位同学看51的程序,在查看汇编代码的时候(事实上我当时的汇编知识基本都还给了老师),无意中问起我“某个局部变量的声明怎么没有对应的汇编语句”,我没有答出来。当时也只是把它当做一种常识给记了下来,平时不论还是在DSP、16位的单片上还是PC平台上编写c程序,由于程序不是很复杂且芯片资源通常足够,因此很少会考虑内存分配、堆栈方面的内容。这几天在看《Linux c 一站式编程》这本书关于变量内容的时候突然想起这个问题,顿时觉得很有必要把深究一下。

因为网络账号还没充钱的缘故,ubuntu系统还不能及时升级,因此在VC平台下做了以下相关试验。代码如下:

int main(void)

{

int d;

int f = d+1;

d = 1;

d = hello(5,10);

return 0;

}

对应的汇编代码如下:

16:   int main(void)

17:   {

00410960   push        ebp

00410961   mov         ebp,esp

00410963   sub         esp,48h

00410966   push        ebx

00410967   push        esi

00410968   push        edi

00410969   lea         edi,[ebp-48h]

0041096C   mov         ecx,12h

00410971   mov         eax,0CCCCCCCCh

00410976   rep stos    dword ptr [edi]

18:

19:

20:       int d;

21:       int f = d+1;

00410978   mov         eax,dword ptr [ebp-4]

0041097B   add         eax,1

0041097E   mov         dword ptr [ebp-8],eax

22:       d = 1;

00410981   mov         dword ptr [ebp-4],1

23:        //printf('Hello world!\n');

24:        //printf("a = %f,b=%f,c=%f",a,b,c);

25:        //getch();

26:       d = hello(5,10);

00410988   push        0Ah

0041098A   push        5

0041098C   call        @ILT+5(_hello) (0040100a)

00410991   add         esp,8

00410994   mov         dword ptr [ebp-4],eax

27:       return 0;

00410997   xor         eax,eax

28:   }

从以上汇编代码20、21两行中可以看到“int d”语句确实没有对应的函数声明。但是在用d给f赋值的时候,d却对应了dword ptr [ebp-4],这一点刚开始的时候很是疑惑,后来在看过几篇相关文章后得知,局部变量的声明和释放是由编译器调整栈指针(Stack Pointer)位置来完成的[1],编译器首先根据变量的类型和数量计算存储局部变量所需的空间,然后调整ESP的值来为局部变量分配空间。“int d;” 在编译的时候应该是将变量d 与地址 dword prt[ebp-4]对应了起来,既d的内存地址为ebp-4到ebp-7;同理,f值往上调整,对应ebp-8到ebp-11。

然而,了解了以上内容还远不够理解函数压栈出栈的原理,于是对main函数下面对应的17行做了分析,考虑linux平台下的情况[],主要针对ESP和EBP两个栈寄存器作分析解释。以下是通过单步调试的方式得到的结果:

0818b9ca8b590ca3270a3433284dd417.png

图1  未单步前

未运行单步执行前,程序和寄存器值如图1所示。ESP = 0012FF84 ,EBP = 0012FFC0;这是旧的(在函数调用时就是主调函数的)栈对应的空间。

0818b9ca8b590ca3270a3433284dd417.png

图2  执行  “00410960   push        ebp”

执行的“push ebp”后,程序和寄存器值如图2所示。ESP = 0012FF80,EBP = 0012FFC0;因为是要在原来栈的基础上开辟新的栈空间,因此,在旧栈(不知道该如何称呼,暂且这么认为)的栈顶上移了四个字节作为新栈的栈底。因此ESP由原来的0012FF84变为0012FF80 。

0818b9ca8b590ca3270a3433284dd417.png

图3  执行  “00410961   mov         ebp,esp”

“mov  ebp,esp”是为了将新的栈底指针赋值为ebp,如图3所示。ESP = 0012FF80,EBP = 0012FF80 。

0818b9ca8b590ca3270a3433284dd417.png

图4  执行  “00410963   sub         esp,48h”

“ sub   esp,48h”则是为了给局部变量在栈中分配一定的存储空间,如图4所示。ESP由原来的0012FF80减小到0012FF38,这里需要注意的是0012FF80到0012FF7C为旧栈中的栈顶对应的四个字节,因此局部变量的分布实际是从地址0012FF7C到地址0012FF34共72个字节空间。

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

图5  执行  “push        ebx ;push        esi;push        edi”

接下来,连续执行三个push,将相应的寄存器值压栈。

时间限制,下面转写了参考中的文献以备忘。

1.在一个栈中,依据函数调用关系,发起调用的函数(caller)的栈帧在下面(高地址方向),被调用的函数的栈帧在上面。

2.每发生一次函数调用,便产生一个新的栈帧,当一个函数返回时,这个函数所对应的栈帧被清除(eliminated)

”[1]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值