假设我在一种方法中有很多堆栈分配。
如果我将花括号添加到一组代码中,则分配的对象超出范围时会从堆栈中弹出,还是在释放内存之前需要退出该方法?
我还应该补充一点,这是在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(
x
和y
共享同一空间):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);
}
}
因此,将大量堆栈用户隔离到单独的功能中会更加安全。