关于堆栈增长和寻址的困惑

时间:2017-07-22 23:11:06

标签: c++ x86 stack mips

我正在努力更好地理解堆栈上的项目以及如何解决它们。我发现的文章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;
    }
}

我也感到困惑的是,并非所有的记忆地址都不是连续的,这让我觉得我做了一些愚蠢的事情。有人可以澄清一下吗?

2 个答案:

答案 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遵循这一方案。

基本上程序中只有一个虚拟内存空间。这取决于操作系统和/或编译器如何使用它。编译器会将逻辑区域中的内存拆分为自己使用,并希望根据平台文档中定义的调用约定来处理它们。

因此,在我们的示例中,vi在函数堆栈上分配。 cout是静态的。每个new int在堆中分配空间。 v不是一个简单的变量,而是一个包含管理列表所需字段的结构。它需要所有这些内部空间。因此,每个push_back修改这些字段以指向以某种方式分配的'int'。 push_back()和back()是函数调用,并为内部变量分配自己的堆栈,以免干扰top函数。