我感到困惑的是,如果你想在堆栈上分配8字节的存储空间,你为什么要使用下面的命令
subq $8,%rsp
字符串
%rsp存储一个地址。为什么从%rsp中存储的地址中减去文字值8会分配8个字节?特别是,为什么$x对应于x个字节?
编辑:我的问题在这个stackoverflow response中得到了回答,它是以字节为单位的,因为这是一个int的大小,它是传入的字面量参数。
编辑2:以上不正确,请看博的评论。
我感到困惑的是,如果你想在堆栈上分配8字节的存储空间,你为什么要使用下面的命令
subq $8,%rsp
字符串
%rsp存储一个地址。为什么从%rsp中存储的地址中减去文字值8会分配8个字节?特别是,为什么$x对应于x个字节?
编辑:我的问题在这个stackoverflow response中得到了回答,它是以字节为单位的,因为这是一个int的大小,它是传入的字面量参数。
编辑2:以上不正确,请看博的评论。
1条答案
按热度按时间q3aa05251#
你说得对。栈指针只是一个指向内存位置的指针,也就是说,它保存的是一个有效地址的整数值。该内存块实际上是在线程初始化时作为一个大块分配的。它通常足够大,以避免堆栈溢出。堆栈指针在开始时指向该块的末尾,每当推入一个新值时就会递减(并且在弹出时递增)。将值推到堆栈只是将给定值移动到RSP指向的内存插槽并递减它。例如:
字符串
这是同义词(除了不影响FLAGS):
型
只从RSP中减去(而不是做
push
)只会在堆栈中留下自由空间,您可以用于自己的目的,而不会删除任何旧值,并且大小可以大于一个8字节堆栈插槽1。这就是局部变量的实际工作方式。所以:型
将堆栈指针向下移动16个字节,这样你就有了16个字节的空间,你可以在自己的函数中使用任何你想要的东西,作为任何大小的组合。要释放它,你要么需要记住相应地递增
rsp
寄存器,要么使用这样的帧指针:型
所以编译器可以执行
push rax
而不是sub rsp, 8
,但这实际上需要写入内存(因为rax的内容需要存储)。这将是一种浪费。相反,移动指针本身是一种更便宜的操作,直到真正需要该内存块。(let我知道我在这里使用的英特尔汇编语法是否会造成混乱,我只是觉得它更容易编写)
脚注1:8字节的倍数是保持堆栈对齐的好主意。实际上,调用约定让被调用者假设RSP在
call
之前对齐16,因此,调用者需要这样做,除非调用您自己的函数时不依赖于它。call
之前的RSP%16==0表示call
之后的RSP%16==8(推送返回地址)。计算push
和sub rsp
,自函数进入以来RSP的总移动量应为8的奇数倍。一些编译器会在函数序言中使用
push rax
来移动RSP,因为在某些CPU上,push rax
比sub rsp, 8
更有效,尽管它做了一个我们不关心的存储。(* Why does this function push RAX to the stack as the first operation?).新存储的垃圾在该空间的值与旧垃圾等价;如果你想聪明点,可以使用push 0
或其他实际上有用的初始化器,而不是稍后的mov
.(What C/C++ compiler can use push pop instructions for creating local variables, instead of just increasing esp once? *)。