让我们从一个例子开始
int globalVar;
void main()
{
int localVar = 10;
globalVar = localVar;
}
这个程序里面有一个全局变量,我们之前分析编译后会放到数据段里面,那么局部变量呢?是放到堆栈!当程序开始执行的时候,内存分布会如图所示:
------------
Code
------------
Data
------------
^
|
Stack - SP
------------
于是例子中就需要对堆栈的操作,可以简化为
fun:
SP -= 4
SP[0] = 10
SP += 4
return
刚开始的时候,SP 指向堆栈起始位置,程序开始的时候,SP 向上增长4个字节,作为函数局部变量存储位置,函数运行过程可以对局部变量进行读写操作,在函数返回前,将堆栈退回到原来的位置。例子中只有一个局部变量,如果有多个局部变量,SP 就需要移动更多的位置,放置局部变量。
让我们看看实际生成的汇编代码,我们用 -fomit-frame-pointer 先忽略 FP 的处理。
main:
# 虽然局部变量只用了4个字节,编译器为了性能优化会申请8个字节,另外4个字节
# 不使用,X86 默认是 16 个字节对齐
sub sp, sp, #8
# 赋值局部变量
movs r3, #10
str r3, [sp, #4]
# 赋值全局变量
ldr r2, .L2
ldr r3, [sp, #4]
str r3, [r2]
# 恢复 SP
add sp, sp, #8
bx lr
.L2:
.word globalVar
函数调用过程栈指针的变化过程:
| |
| |
| |
| |
| |
---- <- SP
函数调用前
| |
| |
| |
|--| <- SP
| |
----
异常函数调用,默认将 PC/LR/R0-R3 入栈
| |
| |
|--| <- SP
|--|
| |
----
函数调用,局部变量申请
| |
| |
| |
|--| <- SP
| |
----
函数调用结束,局部变量释放
| |
| |
| |
| |
| |
---- <- SP
函数调用返回, PC/LR/R0-R3 出栈