C++ Cortex-M4中按值传递对象为何生成更小汇编代码?
关于Cortex-M4上C++引用与值传递的汇编体积差异问题
我在Cortex-M4平台测试C++引用特性时发现,将函数参数从引用传递改为按值传递后,生成的汇编代码体积更小,且这个现象是在为Foo结构体添加更多成员后出现的。同时我还想了解引用是否能被编译器完全优化。以下是测试代码及汇编输出:
引用传递版本代码
struct Foo { int x; int y; int bar[128]; float f; float b; char hello; int * i; }; Foo &getFoo(void) { static Foo _f; return _f; } void passFoo(Foo& f) { f.x = 0; return; } int main(void) { Foo & f = getFoo(); passFoo(f); }
引用传递版本汇编输出
getFoo(): ldr r0, .L3 bx lr .L3: .word .LANCHOR0 passFoo(Foo&): movs r3, #0 str r3, [r0] bx lr main: ldr r3, .L7 movs r0, #0 str r0, [r3] bx lr .L7: .word .LANCHOR0
按值传递版本代码
struct Foo { int x; int y; int bar[128]; float f; float b; char hello; int * i; }; Foo &getFoo(void) { static Foo _f; return _f; } void passFoo(Foo f) { f.x = 0; return; } int main(void) { Foo & f = getFoo(); passFoo(f); }
按值传递版本汇编输出
getFoo(): ldr r0, .L3 bx lr .L3: .word .LANCHOR0 passFoo(Foo): sub sp, sp, #16 stm sp, {r0, r1, r2, r3} add sp, sp, #16 bx lr main: movs r0, #0 bx lr
问题解答
1. 汇编体积差异的原因
- 按值传递的死代码消除:按值传递版本中,
passFoo修改的是传入的Foo副本,这个副本是局部变量,修改后没有任何后续使用(既不返回也不影响外部状态)。编译器直接触发了死代码消除优化:main中原本需要拷贝整个大结构体到passFoo的参数,但因为passFoo的操作完全无意义,编译器直接删除了结构体拷贝和passFoo调用的所有相关代码,main仅保留最简的返回逻辑。- 而引用传递版本中,
passFoo直接修改全局静态Foo的x成员,这个操作会改变外部可见状态,编译器无法消除该操作,因此main中必须保留对静态Foo地址的访问和赋值指令。
- 结构体大小的触发条件:当
Foo较小时,按值传递的拷贝操作开销不大,编译器可能不会做这么激进的消除;但当结构体足够大时,编译器更容易识别到“拷贝大结构体+无意义修改”的冗余性,从而彻底消除整个调用链的冗余代码,最终生成的代码比引用传递版本更短。
2. 引用能否被编译器完全优化
引用在C++标准中是对象的别名,底层通常由指针实现,但在很多场景下编译器可以做到完全优化:
- 当引用的生命周期和指向对象的生命周期完全可被编译器追踪时(比如局部引用绑定到局部变量),编译器会直接把引用替换为原对象,不需要额外的指针操作。
- 但如果引用跨函数传递(如作为函数参数),编译器需要遵守ABI约定传递指针(引用的底层实现);如果操作有副作用(比如修改外部对象),这些指针操作无法消除;如果没有副作用,可能会结合死代码消除等优化去掉冗余部分,但这不是引用本身的优化,而是整体代码的冗余消除。
内容的提问来源于stack exchange,提问作者MrBit
相关产品推荐
相关产品推荐

