我有以下c ++程序,其中函数返回对局部变量的引用。你能否一步一步地向我展示堆栈究竟发生了什么?
#include<stdio.h>
double& init_pi()
{
double pi = 3.14;
return pi;
}
double circumference(double r, double& pi)
{
printf("%lf\n", pi);
return 2*r*pi;
}
int main()
{
printf("%lf\n,", circumference(2, init_pi()));
return 0;
}
感谢您的回答。
答案 0 :(得分:3)
与流行的看法相反,c ++标准从未提到过堆栈的概念。 (除了std :: stack类模板,这不是你的意思)。
标准涉及功能,控制流,本地对象,堆对象和静态对象。
完全有可能为没有堆栈的架构编写一个c ++编译器(当我十几岁的时候我会注意旧的TMS 9900系列芯片)。
你的问题可能会更好:
当使用X编译器编译时,如果使用Z架构的Y选项,堆栈如何在此c ++程序中逐步更改?
答案仅在您的调试器或汇编程序列表中(对于gcc,使用-S选项进行编译)
事实上,如果你在优化的情况下编译这个程序,根本就没有堆栈移动。整个流程将被内联。
例如,带-O2的gcc 5.3产生以下代码(见下文)
请注意,因为您通过返回对局部变量的引用来引入未定义的行为,所以允许编译器执行它喜欢的任何操作。在这种情况下,它决定你的程序什么都不做。 main只返回零。
汇编程序输出:
init_pi():
xorl %eax, %eax
ret
.LC1:
.string "%lf\n"
circumference(double, double&):
pushq %rbx
movl $1, %eax
movq %rdi, %rbx
subq $16, %rsp
movsd %xmm0, 8(%rsp)
movsd (%rdi), %xmm0
movl $.LC1, %edi
call printf
movsd 8(%rsp), %xmm1
movsd (%rbx), %xmm0
addq $16, %rsp
addsd %xmm1, %xmm1
popq %rbx
mulsd %xmm1, %xmm0
ret
main:
movsd 0, %xmm0
ud2
编译器警告:
/tmp/gcc-explorer-compiler11636-75-1libuwy/example.cpp: In function 'double& init_pi()':
5 : warning: reference to local variable 'pi' returned [-Wreturn-local-addr]
double pi = 3.14;
^
Compiled ok
如果我们修正警告和后续错误,我们就会得到:
init_pi():
movsd .LC0(%rip), %xmm0
ret
.LC2:
.string "%lf\n"
circumference(double, double):
subq $24, %rsp
movl $.LC2, %edi
movl $1, %eax
movsd %xmm0, 8(%rsp)
movapd %xmm1, %xmm0
movsd %xmm1, (%rsp)
call printf
movsd 8(%rsp), %xmm2
movsd (%rsp), %xmm1
addq $24, %rsp
addsd %xmm2, %xmm2
movapd %xmm2, %xmm0
mulsd %xmm1, %xmm0
ret
.LC5:
.string "%lf\n,"
main:
subq $8, %rsp
movl $.LC2, %edi
movl $1, %eax
movsd .LC0(%rip), %xmm0
call printf
movsd .LC4(%rip), %xmm0
movl $.LC5, %edi
movl $1, %eax
call printf
xorl %eax, %eax
addq $8, %rsp
ret
.LC0:
.long 1374389535
.long 1074339512
.LC4:
.long 1374389535
.long 1076436664
同样,您会看到main
已完全内联。没有任何堆栈使用(除了在调用printf期间)