我们研究过Stack的扩展和缩小。但在实践中它不会发生 举一个从地址100扩展到0的堆栈的例子,即第一个变量为100,然后是99等等。现在我们编写以下代码
int main()
{
{
int i;
cout<<&i;//100 is displayed on screen
}
{
int j;
cout<<&j;//99 is displayed on screen
}
}
现在,当我们宣布i时,它会转到地址100,然后它的范围结束。然后j在新范围中声明,现在它应该再次具有地址100,因为我已经完成并且堆栈应该回滚但是它没有,j具有地址99.为什么?你能解释一下吗?
答案 0 :(得分:6)
标准没有对局部变量的存储做出这样的承诺。没有理由相信声明的顺序与它们在记忆中的排列顺序有任何关系。
完全按照编译器的要求完成它的工作。实际上,对于您的程序,编译器可以对两个变量使用相同的地址。我希望任何体面的优化编译器都可以这样做。
现在,在实践中,编译器可能会使用您所谈论的各种优化。但是,如何实现这些优化将因编译器而异。并且没有理由将您描述的简单规则普遍应用于所有函数。编译器可以为一个函数选择一个策略,为不同的函数选择不同的策略。编译器的行为可能会有所不同,具体取决于其选项。
当然,如果你在谈论与函数调用相关的堆栈帧,那么它就不同了。显然,当您进行新的函数调用时,会分配一个新的堆栈帧。然后当函数返回时,该堆栈帧被销毁,并且重新输入调用函数的堆栈帧。但这是一个完全不同的问题,而不是你在问题中讨论的内容。
答案 1 :(得分:5)
在堆栈上分配空间需要一些时间,尽管非常小。编译器进行的一个非常常见的优化是分析函数内声明的变量,并在函数开始的同时为所有变量保留堆栈空间。
更聪明的编译器会意识到两个变量的生命周期不重叠,让它们共享相同的位置。
答案 2 :(得分:0)
在函数调用的情况下,堆栈将始终展开并回滚,因为执行路径实际上不可预测,我们需要存储有关局部变量的信息,并在调用树中返回每个正在运行的函数的地址。
这里的情况不同,堆栈指针甚至没有移动,编译器可以自由地优化代码并为两个小变量使用两个不同的地址,即使这会导致最小的内存浪费。