为什么C ++程序会为局部变量分配比最坏情况下更多的内存?

时间:2011-08-17 09:53:49

标签: c++ visual-c++ compiler-construction memory-management stack-overflow

this question的启发。

显然在以下代码中:

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    if( GetTickCount() > 1 ) {
        char buffer[500 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    } else {
        char buffer[700 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    }
    return 0;
}

使用Visual C ++ 10进行编译时使用默认堆栈大小(1兆字节),并在(/ O2)上进行优化,因为程序试图在堆栈上分配1200千字节,因此会发生堆栈溢出。

上面的代码当然有点夸大以显示问题 - 以相当愚蠢的方式使用大量堆栈。然而在实际情况下,堆栈大小可能更小(如256千字节),并且可能会有更多具有较小对象的分支,这会导致总分配大小足以溢出堆栈。

这没有任何意义。最糟糕的情况是700千字节 - 它将是构建一直具有最大总大小的局部变量集的代码路径。在编译期间检测该路径应该不是问题。

因此编译器生成的程序试图分配比最坏情况更多的内存。根据{{​​3}},LLVM也是如此。

这可能是编译器的一个缺陷,或者可能有一些真正的理由这样做。我的意思是,我只是不明白编译器设计中的某些东西可以解释为什么以这种方式进行分配是必要的。

为什么编译器希望程序在最坏的情况下分配比代码需要更多的内存?

4 个答案:

答案 0 :(得分:2)

我只能推测编译器设计者认为这种优化太不重要了。或许,有一些微妙的安全原因。

顺便说一下,在Windows上,当线程开始执行时,堆栈是完整的保留,但是根据需要提交,所以即使你没有真正花费太多“真正的”内存保留了一大堆。

在32位系统上保留大型堆栈可能是一个问题,因为拥有大量线程可能会占用可用的地址空间而不会真正提交大量内存。在64位上,你是金色的。

答案 1 :(得分:2)

这可能取决于您对SecureZeroMemory的使用。尝试用常规ZeroMemory替换它,看看会发生什么 - MSDN页面实质上表明SZM有一些超出它的签名意味着的额外语义,并且它们可能是bug的原因。

答案 2 :(得分:1)

ideone上使用GCC 4.5.1编译时,以下代码将两个数组放在同一地址:

#include <iostream>

int main()
{
  int x;
  std::cin >> x;

  if (x % 2 == 0)
  {
    char buffer[500 * 1024]; 
    std::cout << static_cast<void*>(buffer) << std::endl;
  }

  if (x % 3 == 0)
  {
    char buffer[700 * 1024]; 
    std::cout << static_cast<void*>(buffer) << std::endl;
  }
}
  

输入:6

     

输出:
  0xbf8e9b1c
  0xbf8e9b1c

如果您想进行此优化,答案可能是“使用其他编译器”。

答案 3 :(得分:-3)

OS页面调整和字节对齐可能是一个因素。家政也可以使用额外的堆栈以及调用该功能中其他功能所需的空间。