简体   繁体   English

为什么 C++ 程序为局部变量分配的 memory 比在最坏情况下所需的要多?

[英]Why would a C++ program allocate more memory for local variables than it would need in the worst case?

Inspired by this question .受到这个问题的启发。

Apparently in the following code:显然在以下代码中:

#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;
}

compiled with default stack size (1 megabyte) with Visual C++ 10 with optimizations on (/O2) a stack overflow occurs because the program tries to allocate 1200 kilobytes on stack.使用 Visual C++ 10 以默认堆栈大小(1 兆字节)编译,并在 (/O2) 上进行优化,因为程序试图在堆栈上分配 1200 KB,所以发生堆栈溢出。

The code above is of course slightly exaggerated to show the problem - uses lots of stack in a rather dumb way.上面的代码当然略微夸大了问题 - 以一种相当愚蠢的方式使用了大量堆栈。 Yet in real scenarios stack size can be smaller (like 256 kilobytes) and there could be more branches with smaller objects that would induce a total allocation size enough to overflow the stack.然而,在实际场景中,堆栈大小可能会更小(如 256 KB),并且可能会有更多带有更小对象的分支,这会导致总分配大小足以溢出堆栈。

That makes no sense.这是没有意义的。 The worst case would be 700 kilobytes - it would be the codepath that constructs the set of local variables with the largest total size along the way.最坏的情况是 700 KB——它将是构建沿途总大小最大的局部变量集的代码路径。 Detecting that path during compilation should not be a problem.在编译期间检测该路径应该不是问题。

So the compiler produces a program that tries to allocate even more memory than the worst case.因此编译器生成一个程序,该程序试图分配比最坏情况更多的 memory。 According to this answer LLVM does the same.根据这个答案LLVM 做同样的事情。

That could be a deficiency in the compiler or there could be some real reason for doing it this way.这可能是编译器的一个缺陷,或者这样做可能有一些真正的原因。 I mean maybe I just don't understand something in compilers design that would explain why doing allocation this way is necessary.我的意思是,也许我只是不理解编译器设计中的某些内容,这些内容可以解释为什么需要以这种方式进行分配。

Why would the compiler want a program allocate more memory than the code needs in the worst case?为什么编译器希望程序在最坏情况下分配的 memory 比代码需要的多?

I can only speculate that this optimization was deemed too unimportant by the compiler designers.我只能推测编译器设计者认为这种优化太不重要了。 Or perhaps, there is some subtle security reason.或者,可能存在一些微妙的安全原因。

BTW, on Windows, stack is reserved in its entirety when the thread starts execution, but is committed on as-needed basis, so you are not really spending much "real" memory even if you reserved a large stack.顺便说一句,在 Windows 上,堆栈在线程开始执行时全部保留,但根据需要提交,因此即使您保留了一个大堆栈,您也不会真正花费太多“真正的”memory。

Reserving a large stack can be a problem on 32-bit system, where having large number of threads can eat the available address space without really committing much memory.在 32 位系统上保留大堆栈可能是一个问题,其中拥有大量线程会占用可用地址空间而不会真正提交太多 memory。 On 64-bit, you are golden.在 64 位上,你是金子。

It could be down to your use of SecureZeroMemory.这可能取决于您对 SecureZeroMemory 的使用。 Try replacing it with regular ZeroMemory and see what happens- the MSDN page essentially indicates that SZM has some additional semantics beyond what it's signature implies, and they could be the cause of the bug.尝试用常规的 ZeroMemory 替换它,看看会发生什么 - MSDN 页面基本上表明 SZM 具有超出其签名所暗示的一些额外语义,它们可能是导致错误的原因。

The following code when compiled using GCC 4.5.1 on ideone places the two arrays at the same address:以下代码在 ideone 上使用 GCC 4.5.1 编译时将两个arrays放在同一地址:

#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;
  }
}

input: 6输入:6

output: output:
0xbf8e9b1c 0xbf8e9b1c
0xbf8e9b1c 0xbf8e9b1c

The answer is probably "use another compiler" if you want this optimization.如果您想要这种优化,答案可能是“使用另一个编译器”。

OS Pageing and byte alignment could be a factor.操作系统分页和字节 alignment 可能是一个因素。 Also housekeeping may use extra stack along with space required for calling other functions within that function.此外,内务管理可能会使用额外的堆栈以及调用 function 中的其他函数所需的空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM