它意味着什么"堆栈上分配的所有内存在编译时都是已知的#34;?

时间:2016-07-22 07:24:31

标签: c++ compilation stack allocation

阅读关于Stack vs Heap的this great tutorial,我有一个关于这个短语的注释:堆栈上分配的所有内存在编译时都是已知的

我的意思是,如果我在for周期内依赖于用户输入(i从0到X),并且在for内我分配内存堆栈(例如创建一些类的新实例并放入类容器中),它不知道在编译程序时如何增加堆栈(它会错过用户的输入)。

我是否误解了某些事情?

3 个答案:

答案 0 :(得分:4)

对于读者而言,所做的陈述有点简化。你是对的,堆栈本质上是动态的,实际分配的数量可以根据动态输入而变化。这是一个带递归函数的简单示例:

void f(int n)
{
    int x = n * 10;
    if(x == 0) return;

    std::cout << x << std::endl;
    f(n - 1);
}

int main()
{
    int n;
    std::cout << "Enter n: " << std::endl;
    std::cin >> n;
    f(n);
}

这里显然f(一个递归函数)的调用次数取决于用户输入的n,因此对于任何给定的实例化,编译器都不可能知道本地的确切内存地址x中的变量f。但是, 知道的是x偏离本地堆栈帧,这是我认为该示例所指的。堆栈帧是每次发生函数调用时准备的堆栈的局部区域。在给定的堆栈帧内,局部变量的位置实际上是已知的相对于堆栈帧开始的常量偏移。这个&#39;开始&#39;在每次调用时都保存在标准寄存器中,因此所有编译器必须做的就是找到任何本地的地址,将其固定的已知偏移量应用于这个动态的基本指针&#39;。

答案 1 :(得分:3)

  

我的意思是,如果我在一个依赖于用户输入的for循环中(i从0到X),并且在for我内部分配内存(例如创建一些类的新实例并放入一个class-container),它不知道编译程序时如何增加堆栈(它错过了用户的输入)。

所以你有一个类容器......

std::vector< SomeClass > vec;

......那是在堆栈上。在循环内部,您可以创建一个类的新实例...

for ( size_t i = 0; i < X; ++i )
{
    SomeClass x;

...在堆栈上。当你把它放入容器......

    vec.push_back( x );
}

...该容器将实例存储在堆上。

您在堆栈中只有一个 SomeClass,并且这个事实在编译时是已知的。堆栈不会超过那个实例。

方法在运行时增加堆栈(例如alloca()),因此本教程所做的泛型语句不太正确。

答案 2 :(得分:1)

在考虑的情况下,STACK表示编译器为方法中定义的局部变量分配的内存。 &#34;你分配&#34; (在引号中,因为它是为你完成的)这个内存在定义变量时如:

void myMethod(int x)
{
    int y;
    for (y = 0; y < 10; y++)
    {
       int z = x + y;
    }
}

此示例中的所有xyz都是在堆栈中分配的局部变量。

当你使用new运算符创建一些实例时,你在堆中分配内存(这里你没有引号分配),但是为了存储这个分配内存的地址,你可能会使用在栈中分配的指针变量: / p>

int * p = new int[10];

现在p是局部变量(存储在堆栈中),它存储堆中为10个整数数组分配的内存地址。

当编译器解析你的源代码时,它为调用你的方法制作了一组指令(就特定的处理器而言),并且在这个时候它计算所有局部变量所需的内存大小,并且在程序执行时将分配内存在方法启动之前(在方法完成之后,所有内存都将被释放,即堆栈将在堆栈中(并且将在其中一些中移动数据,例如我的示例中的x将使用方法调用指令中给出的数据进行初始化)在方法执行过程中使用的数据被释放&#34;将丢失&#34;)。