我正在努力更好地理解堆栈上的项目以及如何解决它们。我发现的文章here似乎表明,当初始化MIPS堆栈时,会分配固定数量的内存,堆栈会增长到堆栈限制,这似乎是较小的地址。我假设基于这个逻辑,当遍历0x0000时会发生堆栈溢出?
我意识到MIPS是大端,但这会改变堆栈的增长方式吗?我写了一个我认为在x86_64机器上观察它的快速方法,但是堆栈似乎在成长,就像我最初假设的那样。
#include <iostream>
#include <vector>
int main() {
std::vector<int*> v;
for( int i = 0; i < 10; i++ ) {
v.push_back(new int);
std::cout << v.back() << std::endl;
}
}
我也感到困惑的是,并非所有的记忆地址都不是连续的,这让我觉得我做了一些愚蠢的事情。有人可以澄清一下吗?
答案 0 :(得分:2)
x86机器上的堆栈也向下增长。字节顺序与堆栈增长的方向无关。
机器的堆栈与std::vector<>
完全无关。此外,new int
分配堆内存,因此它绝对不会告诉您堆栈。
为了查看堆栈增长的方向,您需要执行以下操作:
recursive( 5 );
void recursive( int n )
{
if( n == 0 )
return;
int a;
printf( "%p\n", &a );
recursive( n - 1 );
}
(请注意,如果您的编译器足够智能以优化尾递归,那么您需要告诉它不优化它,否则观察将全部错误。)
答案 1 :(得分:1)
基本上你在编程中使用了3种类型的内存:静态,动态/堆和堆栈。
静态内存由编译器预先分配,包含在程序中静态声明的常量和变量。
堆是您可以自由分配和释放的内存
Stack是为函数中声明的所有局部变量分配的内存。这很重要,因为每次调用函数时,都会为其变量分配一个新内存。因此,每次调用函数都会确保它有自己唯一的变量副本。每次从函数返回时,内存都会被释放。
一旦遵循上述规则,堆栈的管理方式无关紧要。然而,方便的是将程序存储器分配到较低的地址空间并长大,并且堆栈从顶部存储空间开始并向下扩展。大多数系统实施此方案。
通常有一个堆栈指针寄存器/变量,它指向当前堆栈地址。当一个函数被调用时,它会减少该地址所需的变量所需的字节数。当它调用下一个函数时,这个新函数将以调用者已经减少的新指针开始。当函数返回时,它将恢复从它开始的指针。
可能有不同的方案,但据我所知,mips和i86遵循这一方案。
基本上程序中只有一个虚拟内存空间。这取决于操作系统和/或编译器如何使用它。编译器会将逻辑区域中的内存拆分为自己使用,并希望根据平台文档中定义的调用约定来处理它们。
因此,在我们的示例中,v
和i
在函数堆栈上分配。 cout
是静态的。每个new int
在堆中分配空间。 v
不是一个简单的变量,而是一个包含管理列表所需字段的结构。它需要所有这些内部空间。因此,每个push_back
修改这些字段以指向以某种方式分配的'int'。 push_back()和back()是函数调用,并为内部变量分配自己的堆栈,以免干扰top函数。