C 语言和 ARM 汇编 - 3 堆栈和局部变量

让我们从一个例子开始

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 出栈