估计每个人在学C语言时被告之:当函数里的代码执行时,函数体内的局部变量会在栈里分配空间,函数执行结束时回收所分配的空间。
但具体是怎样分配,怎样的回收,这些问题就只能发挥想象力了,学会汇编后,其实我们就可以更加的直观地去了解。
对栈不熟悉的话,可以参考程序的段,堆与栈
1). 局部变量的分配空间
test.c
1
2 int main(void)
3 {
4 int a = 56, b = 77;
5
6
7 return 0;
8 }
编译后,用arm编译器的objdump工具得到汇编代码:
0000839c <main>:
839c: e52db004 push {fp} ; (str fp, [sp, #-4]!) //为调用main函数的调用者备份fp寄存器的内容,因下面需改变它的值
83a0: e28db000 add fp, sp, #0 //用fp寄存器存放栈顶最初的位置 , 函数执行时,再把fp寄存器的位置恢复到sp寄存器里,即栈回收了
83a4: e24dd00c sub sp, sp, #12 //在栈里分配空间,用于局部变量a, b. 相当于int a, b;
83a8: e3a03038 mov r3, #56 ; 0x38
83ac: e50b3008 str r3, [fp, #-8] // (fp-8)的位置即是变量a的地址,这里是给局部变量a赋值. a = 56
83b0: e3a0304d mov r3, #77 ; 0x4d
83b4: e50b300c str r3, [fp, #-12] // (fp-12)的位置即是变量b的地址, 给变量b赋值. b = 77;
83b8: e3a03000 mov r3, #0
83bc: e1a00003 mov r0, r3 //返回值放到r0
83c0: e28bd000 add sp, fp, #0 //函数结束前恢复栈顶位置. 即局部变量a,b所分配的空间被回收了.
83c4: e8bd0800 ldmfd sp!, {fp} //恢复fp寄存器的内容
83c8: e12fff1e bx lr //跳回到返回地址. mov pc, lr
2). 函数的参数也是局部变量
test.c
1
2 void func(int aa)
3 {
4 aa = 89;
5 }
6
7 int main(void)
8 {
9
10 return 0;
11 }
编译后的反汇编代码:
0000839c <func>:
839c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
83a0: e28db000 add fp, sp, #0
83a4: e24dd014 sub sp, sp, #20
83a8: e50b0010 str r0, [fp, #-16]
83ac: e3a03059 mov r3, #89 ; 0x59
83b0: e50b3008 str r3, [fp, #-8] // aa = 89; 由此可见函数参数是在栈里分配空间的,也是一个局部变量来的
83b4: e28bd000 add sp, fp, #0
83b8: e8bd0800 ldmfd sp!, {fp}
83bc: e12fff1e bx lr
3). 栈回收重用的问题
test.c
1
2 #include <stdio.h>
3
4 void func2()
5 {
6 int a, b;
7
8 printf("a = %d, b = %d\n", a, b);
9 }
10
11 void func()
12 {
13 int aa = 33, bb = 44;
14 }
15
16 int main(void)
17 {
18 func(); //先执行函数func
19 func2(); //再执行函数func2
20 return 0;
21 }
程序编译后执行的输出: a = 33, b = 44;
反汇编后的代码:
000083f8 <func>: //先执行func函数
83f8: e52db004 push {fp} ; (str fp, [sp, #-4]!)
83fc: e28db000 add fp, sp, #0 //用fp寄存器记录栈顶位置
8400: e24dd00c sub sp, sp, #12 //栈分配12字节
8404: e3a03021 mov r3, #33 ; 0x21
8408: e50b3008 str r3, [fp, #-8] // func函数里的 aa = 33;
840c: e3a0302c mov r3, #44 ; 0x2c
8410: e50b300c str r3, [fp, #-12] // func函数里的 bb = 44;
8414: e28bd000 add sp, fp, #0 //栈顶位置从fp寄存器恢复, aa和bb变量的空间已回收,但原来位置上的值不变
8418: e8bd0800 ldmfd sp!, {fp}
841c: e12fff1e bx lr
000083d0 <func2>: //func函数执行后,func2函数接着执行
83d0: e92d4800 push {fp, lr} //注意,这里多压栈一个寄存器的内容
83d4: e28db004 add fp, sp, #4 //这里sp+4刚好对上 func函数里"add fp, sp, #0"语句时的栈顶位置
83d8: e24dd008 sub sp, sp, #8
83dc: e59f0010 ldr r0, [pc, #16] ; 83f4 <func2+0x24>
83e0: e51b1008 ldr r1, [fp, #-8] //这里的(fp-8)对应func函数里的(fp-8), 取出的值就是33
83e4: e51b200c ldr r2, [fp, #-12] //这里的(fp-12)对应func函数里的(fp-12), 取出的值就是44
83e8: ebffffb5 bl 82c4 <_init+0x20> //调用printf函数
83ec: e24bd004 sub sp, fp, #4
83f0: e8bd8800 pop {fp, pc}
83f4: 00008490 muleq r0, r0, r4