2013-10-26 53 views
1

现在我正在尝试学习x86系统上的汇编语言。因此,我正在准备“从头开始编程”一书。 (可在http://download.savannah.gnu.org/releases/pgubook/免费)计算机堆栈和Assemly语言

在第53页,计算机的堆栈的工作方式进行了说明:

计算机的堆栈住在记忆的最顶端 地址。您可以通过名为pushl的 指令将值压入堆栈顶部。 [...]嗯,我们说它是最高的,但堆栈的“顶部”实际上是堆栈内存的底部。 [...]在内存中,由于架构考虑,堆栈从 内存的顶部开始并向下增长。因此,当我们提到“堆栈顶部”时,请记住它位于堆栈底部的 内存中。

这部分我得到。假设堆栈的内存从地址0开始并在地址11结束(包含)。这意味着堆栈上目前有三个字(每个字节4个字节)。根据我的理解,堆栈顶部的单词当前占用地址8,9,10和11.(因为一个单词有4个字节,因此占用主内存中的四个存储位置)。但是,本书现在说明如下:

堆栈寄存器%esp始终包含一个指向堆栈当前顶端的指针。

好的,在我的例子中,%esp寄存器将保存地址8.它指向当前位于堆栈顶部的单词。但是......

每次我们的东西推到堆栈中有pushl,ESP%得到了4,使其指向堆栈的顶新(记住减去,每个字是四个字节长,堆栈向下增长)。

什么?这不正是其他方式吗?如果我将另一个4字节大小的机器字压入堆栈,这个字将占用主存储器地址12至15.就像他们所说的:堆栈向下增长。现在%esp寄存器指向当前位于堆栈顶部的单词。它从地址12开始。在我们将另一个单词放入堆栈之前,存储在%esp中的地址是8.因此%esp显然已被添加4,而不是相减。他们从哪里得到减法?我错过了什么?我很迷茫......

帮助是非常赞赏;)

+0

“成长向下”仅仅意味着减法。 12 - 4 = 8,ESP得到一个较低的值,然后你推动堆栈上的东西。只要站在你的头上,一切都看起来很正常。 –

回答

3

如果我把另外4个字节大小的maschine字压入堆栈,这个词将占据主存储器地址12〜15。就像他们说的:堆栈向下增长。

向下意味着朝向较低地址,因此在栈上推入另一个值意味着减去4并将该值写入新位置。因此,ESP%变为4

+--------+ 
8 |12345678| <- top of stack before push 
    +--------+ 
4 |11223344| <- top of stack 
    +--------+ 
0 |00000000| 
    +--------+ 
+0

不过,如果你设法让堆栈到达'0x0'你正在做的事情严重错误:) –

+0

我还是很困惑:比方说,0至39在内存堆栈被保留的地址。这意味着有足够的空间可以将10个4字节大小的机器字压入堆栈顶部。这意味着顶层元素占据地址39,38,37,36。如果我们将另一个单词放到该堆栈上,它将占用地址35,34,33,32。因此%esp寄存器在第一个地址时指向36地址,然后解决32.我知道这是一个抽象的例子,但这是理论上正确的吗? – lambdarookie

+0

是的,正确的。通常情况下,堆栈将从内存顶部开始并逐渐减小,而代码和数据将在内存中开始降低并建立起来。当你的数据长大,你的堆栈越来越小的时候,你的记忆就已经不复存在。 –

0

通常“的内存地址的顶部”指的是最高地址。例如,您的堆栈可能从0x00105000开始;如果你添加一个单词,你将esp移动到0x00104ffc(即您在内存地址中向下增长)。见例如here为一个很好的图。

0

如果您输入的功能和堆栈在0x100的。通常情况下,堆栈从高地址向低地址向下增长,趋近于零。所以如果你推一个4字节的项目,堆栈“top”现在是0xFC。推另一个,0xF8,等等。

是的方式获得其他的事情,这里是EBP VS ESP。

一般一个处理器有一个堆栈指针,无论是具有依赖于它的具体说明,压入,弹出,也许堆栈指针相对寻址加载和存储专用或有时通用寄存器。编译器为堆栈帧使用与堆栈指针不同的通用(或特殊用途)寄存器并不罕见。为什么?使编译器生成的代码的读取和调试更容易一些。

例如,当您输入函数时,您可能会将堆栈指针复制到另一个寄存器中,并且在寄存器不移动的函数期间,这允许您对该寄存器执行相对寻址以查找本地变量以及函数参数(如果此编译器和处理器倾向于在堆栈上传递参数)。因此,在整个函数中,您可以使用相同的偏移量访问这些局部变量中的任何一个。如果你没有使用栈帧,然后,如果,优化的原因(堆栈内存保护)堆栈指针是在函数内部动态,然后从堆栈指针的局部变量的相对偏移和叠基于函数的参数也将是动态更难以阅读和调试。 (同时节省堆栈空间并返回一个寄存器用于其他用途,包括两种优化)。

在任何情况下,当调用嵌套函数时,调用函数的函数需要将堆栈指针设置在“堆栈顶部”,以便该函数可以像其他函数一样享受堆栈,并假定它可以使用/为了自己的目的,销毁超出栈底的任何东西。