IDE:Keil MDK V5.33
内核:Cortex-M3
引言
局部变量(局部静态变量除外)一般是保存在栈区的,但这不是绝对的,有时编译器会对其进行优化,不为其开辟栈空间。
1 初步分析
不加volatile
关键字修饰局部变量的C函数与反汇编代码如下:
- C代码
int mymain()
{
int a = 0x53, b = 3;
return a / b;
}
- 汇编代码
MOVS r2,#0x53 ; r2 = 0x53
MOVS r1,#3 ; r1 = 3
SDIV r0,r2,r1 ; r0 = r2 / r1 = 0x53 / 3(SDIV为带符号除法,需硬件支持)
所有计算过程均在CPU内部,没有出现访问内存的操作,当然也没开辟栈空间。
加volatile
关键字后的汇编代码如下:
PUSH {r2,r3,lr}
MOVS r0,#0x53
STR r0,[sp,#4]
MOVS r0,#3
STR r0,[sp,#0]
LDRD r1,r0,[sp,#0]
SDIV r0,r0,r1
POP {r2,r3,pc}
- 用栈来存储局部变量,先让寄存器r2,r3,lr入栈:
- ARM芯片均为向下增长的满栈
- 标号小的寄存器后入栈(先出栈),lr即r14
- r0 = 0x53,保存至sp+4所示地址,即
*(sp+4) = r0 = 0x53
- r0 = 3,保存至sp所示地址,即
*sp = r0= 3
- 执行
LDRD r1,r0,[sp,#0]
指令,从sp位置取出双字数据(2个4字节),r1加载低32位数据,r0加载高32位数据,即r1 = *sp = 3,r0 = *(sp+4) = 0x53 - r0 = r0 / r1 = 0x53 / 3
- 最后弹栈释放栈区内存,并将pc = lr,实现程序跳转
编译器对局部变量的优化是将其值直接加载到cpu内部寄存器的,那么是不是只要当这些寄存器空闲就满足优化条件呢?
2 验证猜想
定义一个具有13
个局部变量的函数:
int mymain()
{
int c0 = 7, c1 = 7, c2 = 7, c3 = 7, c4 = 7, c5 = 7, c6 = 7, c7 = 7;
int c8 = 7, c9 = 7, c10 = 7, c11 = 7, c12 = 7;
return 0;
}
查看其反汇编代码:
这些局部变量并没存放到栈中,而是用r0~r12、lr
来保存它们的值。
将变量个数再+1后的反汇编代码:
为新增的变量开辟了栈空间。
3 总结
- 局部变量不加
volatile
时,如果当前有空闲的寄存器(sp
和pc
除外),编译器会对其进行优化,即直接将值加载到这些寄存器中,在cpu内部完成运算,不需要访问内存和开辟栈空间; - 局部变量加
volatile
时,编译器不会对其进行优化,且永远都是先在栈中保存局部变量的值,再从栈中读出这些数据进行相应运算,最后弹栈释放开辟的栈内存。
END