我试图将我的思绪围绕在 Assembly 中的指针上。
究竟有什么区别:
mov eax, ebx
和mov [eax], ebx
什么时候应该dword ptr [eax]
应该使用?同样,当我尝试做
mov eax, [ebx]
时我得到一个编译错误,这是为什么?
最佳答案
如前所述,将括号括在操作数周围意味着该操作数将被取消引用,就好像它是 C 中的指针一样。换句话说,括号意味着您正在从中读取值(或将值存储到)该内存位置,而不是直接读取该值。
所以这:
mov eax, ebx
只需复制
ebx
中的值进入 eax
.在伪 C 符号中,这将是:eax = ebx
.而这:
mov eax, [ebx]
取消引用
ebx
的内容并将指向的值存储在 eax
中.在伪 C 符号中,这将是:eax = *ebx
.最后,这个:
mov [eax], ebx
将值存储在
ebx
中进入eax
指向的内存位置.同样,在伪 C 符号中:*eax = ebx
.此处的寄存器也可以替换为内存操作数,例如符号变量名称。所以这:
mov eax, [myVar]
取消引用变量
myVar
的地址并将该变量的内容存储在 eax
中, 喜欢 eax = myVar
.相比之下,这个:
mov eax, myVar
存储变量
myVar
的地址进入 eax
, 喜欢 eax = &myVar
.至少,这是大多数汇编程序的工作方式。 Microsoft 的汇编器(称为 MASM)和 Microsoft C/C++ 编译器的内联汇编有点不同。它将上述两条指令视为等价的,基本上忽略了内存操作数周围的括号。
要在 MASM 中获取变量的地址,可以使用
OFFSET
关键词:mov eax, OFFSET myVar
然而,即使 MASM 具有这种宽容的语法并允许您马虎,但您不应该这样做。当您想要取消引用变量并获取其实际值时,请始终包括括号。如果您使用正确的语法明确地编写代码,您将永远不会得到错误的结果,并且它会让其他人更容易理解。另外,它会迫使您养成以其他汇编程序期望的方式编写代码的习惯,而不是依赖 MASM 的“按我的意思做,而不是按我写的”拐杖。
说到“做我的意思,而不是我写的”的关键,MASM 通常还允许您省略操作数大小说明符,因为它知道变量的大小。但同样,我建议为了清晰和一致性而编写它。因此,如果
myVar
是 int
,你会这样做:mov eax, DWORD PTR [myVar] ; eax = myVar
或者
mov DWORD PTR [myVar], eax ; myVar = eax
这个符号是 necessary in other assemblers like NASM不是强类型的并且不记得
myVar
是 DWORD
-大小的内存位置。在取消引用寄存器操作数时,您根本不需要这个,因为寄存器的名称表明了它的大小。
al
和 ah
总是 BYTE
-大小,ax
总是 WORD
-大小,eax
总是 DWORD
-大小,和 rax
总是 QWORD
-大小。但无论如何,如果您愿意,为了与您标记内存操作数的方式保持一致,包含它也没有什么坏处。Also when I try to do
mov eax, [ebx]
I get a compile error, why is this?
嗯……你不应该。这在 MSVC 的内联汇编中对我来说很好。正如我们已经看到的,它等价于:
mov eax, DWORD PTR [ebx]
and 表示
ebx
指向的内存位置将被取消引用,并且 DWORD
-size 值将加载到 eax
.why I cant do
mov a, [eax]
Should that not make "a" a pointer to wherever eax is pointing?
不可以。这种操作数组合是不允许的。正如您从 the documentation for the
MOV
instruction 中看到的,基本上有五种可能性(忽略替代编码和段):mov register, register ; copy one register to another
mov register, memory ; load value from memory into register
mov memory, register ; store value from register into memory
mov register, immediate ; move immediate value (constant) into register
mov memory, immediate ; store immediate value (constant) in memory
请注意,没有
mov memory, memory
,这就是你正在尝试的。但是,您可以制作
a
指向什么eax
通过简单的编码指向:mov DWORD PTR [a], eax
现在
a
和 eax
具有相同的值(value)。如 eax
是一个指针,然后 a
现在是指向同一内存位置的指针。如果要设置
a
到 eax
的值指向,那么您需要执行以下操作:mov eax, DWORD PTR [eax] ; eax = *eax
mov DWORD PTR [a], eax ; a = eax
当然,这会破坏指针并将其替换为取消引用的值。如果不想丢失指针,则必须使用第二个“暂存”寄存器;就像是:
mov edx, DWORD PTR [eax] ; edx = *eax
mov DWORD PTR [a], edx ; a = edx
我意识到这一切都有些令人困惑。
mov
指令在 x86 ISA 中具有大量潜在含义。这是由于 x86 作为 CISC 架构的根源。相比之下,现代 RISC 架构在分离寄存器-寄存器移动、内存加载和内存存储方面做得更好。 x86 将它们全部塞进一个 mov
操作说明。现在回去修复已经太晚了;你只需要熟悉语法,有时需要再看一眼。
关于pointers - x86 汇编指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43769467/