2014-02-28 50 views
3

汇编可以将数据存储在寄存器或堆栈中。只有堆栈的顶部可以在任何时候访问(对吧?)。考虑下面的C代码:组件如何访问/存储堆栈中的变量

main(){ 
    int x=2; 
    func(); 
} 

func(int x){ 
    int i; 
    char a; 
} 

当打电话FUNC()被压入堆栈以下(考虑32位系统):

variable x (4 bytes, pushed by main) 
<RETURN ADDRESS> (4 bytes pushed by main?) 
<BASE POINTER> (4 bytes pushed by func()) 
variable i (4 bytes, pushed by func()) 
variable a (1 byte, pushed by func()) 

我有以下问题:

  1. 在C代码中,您可以从函数内的任何位置访问本地变量,但是在汇编中,您只能访问堆栈的顶部。 C代码被翻译成汇编(在机器代码中,但汇编是它的可读形式)。那么程序集如何支持读取不在栈顶的变量?

  2. 在我的例子中,我是否遗漏了任何会被推入堆栈的东西?

  3. 在程序集中,如果你推动堆栈上的一个char或一个int,它如何确定它需要推送4个字节还是1个字节?因为它使用相同的操作(推)正确吗?

在此先感谢 Gr。 Maricruzz

回答

2

编译器正在生成程序集,每个指令集可能不同,但在一天结束时,堆栈只是一个寄存器,它持有一个地址到内存。编译器正在创建并且知道它正在创建的函数的整个范围,并且知道在本地数据项的堆栈中找到每个数据项有多远,因此它会根据指令集创建适当的代码来访问这些本地数据项项目。一些指令集需要复制堆栈指针和/或用堆栈指针作为操作数做数学运算,然后根据该数学运算得到其他一些寄存器,然后根据该数学运算(堆栈指针+8单词)例如)您访问该内存地址。一些指令集有一个寻址模式,您可以在加载或存储时向栈指针施加偏移量,数学运算作为指令执行的一部分完成,您不必使用中间结果和寄存器。

1

只有堆栈的顶部可以在任何时候进行访问(右?)

没有,一般的ISA有说明访问堆栈上的其他元素。也就是说,访问堆栈中的元素不限于pushpop类似的操作;通常你可以在堆栈位置和寄存器之间来回穿梭mov

3

将函数开头的堆栈指针放入寄存器,然后通过此基址加上变量的偏移量访问变量/参数。

如果您想查看代码而不是创建目标文件,请让编译器停止创建汇编程序文件。然后你可以看到它究竟是如何工作的。 (当然,这需要你有一个有效的C程序,不像你现在问题中的那个)

+0

谢谢!是否也有可能在某处看到堆栈的当前内容?任何工具呢? – Maricruzz

+0

@Maricruzz任何体面的调试器都应具有该功能。 –

1

Assembly可以通过地址访问任何内存(就像C)。

简单的未优化程序会在方法执行前将所有本地变量放在堆栈上,所以变量地址是执行帧的地址加上一些移位。

然后程序可以简单地使用poppush方法来在堆栈顶部存储附加变量(即一些表达式的子结果)。

总结:

  1. 有寄存器(ESPx86)指向堆栈
  2. 调用push移动变量到堆栈的顶部和增加的顶部这个寄存器
  3. 调用pop从堆栈顶部移动变量并减少该寄存器
  4. 调用mov正在存储器和寄存器之间移动变量,并且执行没有任何要堆栈寄存器(ESP)。
+0

按操作数的大小esp递减,将操作数存储在[esp]。弹出从[esp]加载操作数,然后按操作数的大小增加esp。在禁用“帧指针”的情况下,C会生成使用esp中的偏移量来定位变量的代码。启用“帧指针”后,函数会执行esp | mov ebp,esp和基于ebp的大部分偏移量。 – rcgldr