结论
结论写在前面
1.在 for 循环语句内定义局部循环变量,使用 AC6 编译器或者 GCC 编译器,都不会因此产生多次栈操作,而是会使用相同的两个堆栈偏址。如果开优化,当二者逻辑功能没有实际差异时,汇编将完全一样。
事实上在 for 循环的同时定义循环变量是一个优秀的操作。对于将所有局部变量的定义全部提前到函数头部,会产生事实上的负优化或无优化(依据优化等级和编译器不同)。
由此引申,如果追求极致的性能,应当仅在使用到局部变量的分支中声明该局部变量。
以下测试均以 stm32H7为目标编译
用如下的写法讨论局部变量的栈操作
for(int i = 0; i < 50; i++)
{
for(int j = 0; j < 50; j++)
{
HAL_Delay(1);
}
}
感性来看,第一个循环每进行一次,都会声明一个局部变量 j,那么是否会由此产生多次的栈申请操作呢?
这部分的反汇编如下
0x0000001e: LDR r0,[sp,#0]
0x00000020: STR r0,[sp,#8]
0x00000022: B {pc}+0x2 ; 0x24
0x00000024: LDR r0,[sp,#8]
0x00000026: CMP r0,#0x31
0x00000028: BGT {pc}+0x2c ; 0x54
0x0000002a: B {pc}+0x2 ; 0x2c
0x0000002c: MOVS r0,#0
0x0000002e: STR r0,[sp,#4]
0x00000030: B {pc}+0x2 ; 0x32
0x00000032: LDR r0,[sp,#4]
0x00000034: CMP r0,#0x31
0x00000036: BGT {pc}+0x14 ; 0x4a
0x00000038: B {pc}+0x2 ; 0x3a
0x0000003a: MOVS r0,#1
0x0000003c: BL HAL_Delay
0x00000040: B {pc}+0x2 ; 0x42
0x00000042: LDR r0,[sp,#4]
0x00000044: ADDS r0,#1
0x00000046: STR r0,[sp,#4]
0x00000048: B {pc}-0x16 ; 0x32
0x0000004a: B {pc}+0x2 ; 0x4c
0x0000004c: LDR r0,[sp,#8]
0x0000004e: ADDS r0,#1
0x00000050: STR r0,[sp,#8]
0x00000052: B {pc}-0x2e ; 0x24
外层循环
不是我们的主要讨论对象,反正就是会使用跳转将内层循环执行50次
0x0000001e: LDR r0,[sp,#0]
0x00000020: STR r0,[sp,#8]
0x00000022: B {pc}+0x2 ; 0x24
0x00000024: LDR r0,[sp,#8]
0x00000026: CMP r0,#0x31
0x00000028: BGT {pc}+0x2c ; 0x54
0x0000002a: B {pc}+0x2 ; 0x2c
; .....内层循环
0x0000004a: B {pc}+0x2 ; 0x4c
0x0000004c: LDR r0,[sp,#8]
0x0000004e: ADDS r0,#1
0x00000050: STR r0,[sp,#8]
0x00000052: B {pc}-0x2e ; 0x24
内层循环
CodeBlock Loading...
2c、2e 两句 将 sp+4处堆栈的值置0
然后利用增1和跳转,执行50次循环
也就是每次执行外层循环都会有这套针对 sp+4处堆栈的操作逻辑,外层循环每次都是针对 sp+8处堆栈的操作逻辑
如果是预先定义局部变量呢?
改为如下写法
CodeBlock Loading...
这部分的反汇编如下
CodeBlock Loading...
可以看出,其循环部分(26-56)与此前的写法(22-52)部分没有差异,反而多将(sp+4)与(sp+8)置零的两条语句,产生了负优化。
将循环复杂化是否会不一样
如下代码,与反汇编,占用的(sp+8)与(sp+12),依然不会产生过多的堆栈操作
CodeBlock Loading...
CodeBlock Loading...
如下代码,将声明提前,依然产生负优化了
CodeBlock Loading...
CodeBlock Loading...
使用优化
O1
仍然是上面的循环复杂化
for 内声明
CodeBlock Loading...
将声明提前,二者完全一致
CodeBlock Loading...
O2
仍然是上面的循环复杂化
for 内声明
CodeBlock Loading...
将声明提前,二者完全一致
CodeBlock Loading...
O3
O3没有讨论价值,完全展开循环。
GCC 环境下的情况
将局部变量提前定义,同样是负优化
局部变量定义在 for 内,20条

局部变量先定义,24条
