假设我在一种方法中有很多堆栈分配。

如果我将花括号添加到一组代码中,则分配的对象超出范围时会从堆栈中弹出,还是在释放内存之前需要退出该方法?

我还应该补充一点,这是在MFC应用程序中完成的。

void LongMethod()
{
    {
        struct someLargeStruct;
        // Do a lot of work and allocations.
    }

    {
        struct anotherLargeStruct;
        // more work here.
    }
}

最佳答案

只是为了进一步澄清这一点-是的,该标准要求在块作用域的末尾释放自动存储,请参见[basic.stc.auto]/1:



但是,要求编译器仅实现程序的可观察行为(the as-if rule):



由于该标准将自动存储视为无限存储,并且没有其他方法可以观察堆栈的使用情况,因此,在符合规则的情况下,符合条件的编译器不必严格要求在每个作用域的末尾完全释放内存。

实际上,我们观察到GCC,clang和MSVC倾向于在函数启动时分配堆栈空间,而在函数退出时分配。尽管至少它们似乎在不同的块作用域之间重用了内存:

int do_something_with(int*);
int do_something_with(long long*);

void do_something()
{
    {
        int x[100];
        do_something_with(x);
    }
    {
        long long y[100];
        do_something_with(y);
    }
}

分配的堆栈帧:800 bytes(xy共享同一空间):
do_something():
        sub     rsp, 808
        mov     rdi, rsp
        call    do_something_with(int*)
        mov     rdi, rsp
        call    do_something_with(long long*)
        add     rsp, 808
        ret

如果没有块作用域,行为会稍有变化,我们发现不再有内存重用:
int do_something_with(int*);
int do_something_with(long long*);

void do_something()
{
    int x[100];
    do_something_with(x);
    long long y[100];
    do_something_with(y);
}

分配的堆栈帧:1200 bytes(x + y):
do_something():
        sub     rsp, 1208
        mov     rdi, rsp
        call    do_something_with(int*)
        lea     rdi, [rsp+400]
        call    do_something_with(long long*)
        add     rsp, 1208
        ret

因此,可以得出结论,是的,块作用域具有一定的作用,但不要期望它与这些作用域完全一致。

在递归函数(example)中尤其令人讨厌:
int do_something_with(long long*);
int bar();

void foo()
{
    {
        if (bar())
            foo(); // don't count on this being efficient
    }
    {
        long long y[10000];
        do_something_with(y);
    }
}

因此,将大量堆栈用户隔离到单独的功能中会更加安全。

09-10 04:28
查看更多