阅读关于Stack vs Heap的this great tutorial,我有一个关于这个短语的注释:堆栈上分配的所有内存在编译时都是已知的。
我的意思是,如果我在for
周期内依赖于用户输入(i
从0到X),并且在for
内我分配内存堆栈(例如创建一些类的新实例并放入类容器中),它不知道在编译程序时如何增加堆栈(它会错过用户的输入)。
我是否误解了某些事情?
答案 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;
}
}
此示例中的所有x
,y
和z
都是在堆栈中分配的局部变量。
当你使用new
运算符创建一些实例时,你在堆中分配内存(这里你没有引号分配),但是为了存储这个分配内存的地址,你可能会使用在栈中分配的指针变量: / p>
int * p = new int[10];
现在p
是局部变量(存储在堆栈中),它存储堆中为10个整数数组分配的内存地址。
当编译器解析你的源代码时,它为调用你的方法制作了一组指令(就特定的处理器而言),并且在这个时候它计算所有局部变量所需的内存大小,并且在程序执行时将分配内存在方法启动之前(在方法完成之后,所有内存都将被释放,即堆栈将在堆栈中(并且将在其中一些中移动数据,例如我的示例中的x
将使用方法调用指令中给出的数据进行初始化)在方法执行过程中使用的数据被释放&#34;将丢失&#34;)。