【问题标题】:Can stack memory be allocated within a function automatically?堆栈内存可以在函数内自动分配吗?
【发布时间】:2013-10-18 13:53:39
【问题描述】:

如果之前有人问过这个问题,我很抱歉,但我没有找到任何东西......

对于“普通”x86 架构:

当我在 C++ 中调用一个大函数时,是否会立即为所有堆栈变量分配内存? 或者是否有编译器可以(并且确实)修改堆栈大小,即使函数没有完成。

例如,如果一个新的范围开始:

int largeFunction(){
    int a = 1;
    int b = 2;

    // .... long code ....

    { // new scope

         int c = 5;

         // .... code again ....

    }

    // .....

}

对于变量 c 的调用堆栈是否也可以在单独作用域的开头“增长”并在其结尾“收缩”? 还是当前的编译器总是会生成影响函数入口和返回值的堆栈指针的代码? 提前感谢您的回答。

【问题讨论】:

  • 大多数编译器都有保存生成的程序集文件的选项。试试看,看看他们做了什么。我敢打赌,如果你有一个分配大数组的if 块,它只会在测试成功时增加堆栈帧。
  • 您为什么需要知道这些?我想这是依赖于实现的,因此依赖于任何特定行为的代码既不便携也不符合标准。换句话说,作为 C++ 程序员,您不必担心这一点。
  • 堆栈大小是固定的。但这只是虚拟内存,用于堆栈的 RAM 肯定会动态增长。按需页面虚拟内存操作系统的标准功能。
  • @walter:即使你不必依赖它,了解你工作所在的抽象层下的抽象层也能让你更好地理解你工作的那个。
  • @mbratch:OTOH 动态调整堆栈大小会增加额外的簿记指令,从而增加代码大小和程序运行时间。

标签: c++ c memory-management stack


【解决方案1】:

1) 函数的运行时间与内存分配无关,与栈或堆无关。

2) 何时“分配”堆栈仅取决于编译器生成最有效代码的方式。 “高效”有着广泛的要求。所有编译器都可以选择修改优化器的速度和大小目标,并且大多数编译器还可以针对更低的堆栈消耗和其他参数进行优化。

3) 自动变量可以入栈,但这不是必须的。很多变量应该被“分配”到你的 cpu 的寄存器中。这大大加快了代码速度并节省了堆栈。但这很大程度上取决于cpu平台。

4) 编译器何时生成新的栈帧也是代码优化的问题。如果这样可以节省资源或更适合架构,编译器可以执行“乱序执行”。因此,无法回答何时使用堆栈帧的问题。新的作用域(左大括号)可以作为分配新堆栈帧的点,但这绝不是保证。有时从实际作用域重新计算所有被调用函数的所有堆栈相对地址效率不高。

5) 一些编译器还可以将堆内存用于自动变量。如果通过特殊指令进行访问比堆栈相对寻址更快,则这种情况经常出现在嵌入式内核上。

但是通常编译器做他想做的事情并不是很重要。有时要记住的唯一一件事是,你必须保证你的筹码足够大。通常对新线程的系统调用有参数来设置堆栈大小。所以你必须知道你的实现需要多少堆栈大小。但在所有其他情况下:忘记思考。这项工作由您的编译器开发人员完美完成。

【讨论】:

    【解决方案2】:

    我不知道答案(我希望您只是想知道,因为您很好奇,因为没有有效的程序应该能够区分),但是您可以通过调用来测试编译器的行为在新作用域之前和新作用域之后像这样运行:

    std::intptr_t stackaddr()
    {
        int i;
        return reinterpret_cast<std::intptr_t>(&i);
    }
    

    如果您得到相同的结果,则表示堆栈已在创建 c 之前进行了调整。

    G++ 4.7 中有一个变化,它允许编译器在其作用域结束后重新使用 c 的堆栈空间,以前在该点之后的任何新变量都会增加堆栈使用量:"G++ now properly re-uses stack space allocated for temporary objects when their lifetime ends, which can significantly lower stack consumption for some C++ functions." 但我认为这只影响在进入函数时保留多少堆栈,而不是何时/何地保留。

    【讨论】:

    • 引入这样的测试和调试功能会极大地改变编译器的优化。我相信对这种调试输出的解释几乎与“真实”代码和实际堆栈消耗无关。但是,是的,它有效:-)
    【解决方案3】:

    这完全取决于您使用的系统的运行时约定,但是,CPU 架构通常在决定中发挥重要作用,因为架构定义了可以安全使用的堆栈管理。例如,在 MacOS X 下的旧 PowerPC 上,堆栈帧始终具有固定大小,新堆栈帧低端的堆栈指针的一个原子存储将分配它,取消引用堆栈指针相当于弹出整个堆栈帧。

    当前系统,如 Linux 和(请纠正我,如果我错了)x86 上的 Windows 具有更动态的方法,其中包含原子推送和弹出指令(PowerPC 上没有原子弹出),其中函数调用的参数是在每次函数调用之前推入堆栈,每次都有效地调整分配的堆栈帧。

    所以,是的,在许多当前系统上,编译器可以调整堆栈帧的大小,但在其他系统上,这样的操作至少很难完成(但绝不是不可能的)。

    【讨论】:

      猜你喜欢
      • 2019-05-17
      • 2011-01-07
      • 2018-07-24
      • 2011-05-28
      • 2017-05-16
      • 2013-10-29
      • 2014-10-26
      • 1970-01-01
      相关资源
      最近更新 更多