在汇编中您可以将数据存储在寄存器或堆栈中。在任何给定时刻只能访问堆栈的顶部(对吗?)。请考虑以下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())
我有以下问题:
在C代码中,您可以从函数内部的任何位置访问本地变量,但在汇编中,您只能访问堆栈的顶部。 C代码被转换为程序集(在机器代码中,但程序集是它的可读形式)。那么程序集如何支持读取不在堆栈顶部的变量?
在我的例子中,我是否遗漏了任何可能被推入堆栈的内容?
在汇编中,如果你在堆栈或int上推送一个char,它怎么能确定它需要推送4个字节还是1个字节?因为它使用相同的操作(推)对吗?
提前致谢 克。 Maricruzz
答案 0 :(得分:3)
将函数开头的堆栈指针放入寄存器,然后通过此基址加上变量的偏移量来访问变量/参数。
如果要查看代码,而不是创建目标文件,请让编译器停止创建汇编程序文件。然后你就可以看到它是如何工作的。 (当然,这需要你有一个有效的C程序,不像你现在在问题中那样。)
答案 1 :(得分:2)
编译器正在生成程序集,每个指令集可能不同,但在一天结束时,堆栈只是一个寄存器,其中包含一个存储器地址。编译器正在创建并知道它正在创建的函数的整个范围,并知道在堆栈中查找本地数据项的每个数据项的距离,因此它将根据该指令集创建适当的代码以访问本地数据项。项目
您需要制作堆栈指针的副本和/或使用堆栈指针作为操作数进行数学运算的一些指令集,但是作为该数学运算的一些其他寄存器,然后基于该数学运算(堆栈指针+ 8个字)例如,您访问该内存地址。某些指令集具有寻址模式,您可以在加载或存储中将偏移量应用于堆栈指针,数学运算是指令执行的一部分,您不必使用中间结果和寄存器。
答案 2 :(得分:1)
在任何给定时刻只能访问堆栈顶部(对吗?)
不,通常ISA也有访问堆栈上其他元素的指令。也就是说,访问堆栈中的元素不仅限于push
和pop
之类的操作;通常,你可以在堆栈位置和寄存器之间来回mov
。
答案 3 :(得分:1)
Assembly
可以按地址访问任何内存(就像C
)。
简单的,未优化的程序会在方法执行之前将所有局部变量放在堆栈上,因此变量地址是执行帧的地址加上一些移位。
然后程序可以简单地使用pop
和push
方法在堆栈顶部存储其他变量(即某些表达式的子结果)。
要点:
ESP
中的x86
)push
将变量移动到堆栈顶部并增加此寄存器pop
是从堆栈顶部移动变量并减少此寄存器mov
是在内存和寄存器之间移动变量而不对堆栈寄存器(ESP
)执行任何操作。