堆栈如何在此c ++程序中逐步更改?

时间:2016-04-06 17:23:55

标签: c++ stack

我有以下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;
}

感谢您的回答。

1 个答案:

答案 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期间)