x86程序集指针

Dux*_*uxa 15 x86 assembly pointers

希望这不是一个愚蠢的问题,但我试图围绕汇编指针.

究竟有什么区别:

mov eax, ebx
Run Code Online (Sandbox Code Playgroud)

mov [eax], ebx
Run Code Online (Sandbox Code Playgroud)

何时应该dword ptr [eax]使用?

另外,当我尝试这样做时,mov eax, [ebx]我得到一个编译错误,为什么会这样?

Cod*_*ray 25

正如已经陈述的,缠绕操作数括号意味着,操作数被解除引用,如同它是在C的指针.换句话说,支架意味着你正在阅读的值(或存储一个值)那个内存位置,而不是直接读取那个值.

所以这:

mov  eax, ebx
Run Code Online (Sandbox Code Playgroud)

只需将值复制ebxeax.在伪C表示法中,这将是:eax = ebx.

鉴于此:

mov  eax, [ebx]
Run Code Online (Sandbox Code Playgroud)

取消引用的内容ebx并存储指向的值eax.在伪C表示法中,这将是:eax = *ebx.

最后,这个:

mov  [eax], ebx
Run Code Online (Sandbox Code Playgroud)

将值存储ebx到指向的内存位置eax.再次,用伪C表示法:*eax = ebx.


这里的寄存器也可以用存储器操作数替换,例如符号变量名.所以这:

mov  eax, [myVar]
Run Code Online (Sandbox Code Playgroud)

取消引用变量的地址并将该变量myVar内容存储在eax,例如eax = myVar.

相比之下,这:

mov  eax, myVar
Run Code Online (Sandbox Code Playgroud)

将变量的地址存储myVareax,例如eax = &myVar.

至少,这是大多数装配工的工作方式.Microsoft的汇编程序(称为MASM)和Microsoft C/C++编译器的内联汇编有点不同.它将上述两条指令视为等效,基本上忽略了内存操作数的括号.

要获取MASM中变量的地址,您可以使用以下OFFSET关键字:

mov  eax, OFFSET myVar
Run Code Online (Sandbox Code Playgroud)

然而,即使MASM具有这种宽容的语法并允许你马虎,你也不应该.如果要取消引用变量并获取其实际值,请始终包括括号.如果使用正确的语法显式编写代码,您将永远不会得到错误的结果,并且它将使其他人更容易理解.此外,它会迫使你养成按照其他汇编程序期望编写代码的方式编写代码的习惯,而不是依赖于MASM的"做我的意思,而不是我写的"拐杖.

说到"我的意思,不是我写的"拐杖,MASM通常也允许你省略操作数大小说明符,因为它知道变量的大小.但同样,我建议为了清晰和一致性而编写它.因此,如果myVarint,你会这样做:

mov  eax, DWORD PTR [myVar]    ; eax = myVar
Run Code Online (Sandbox Code Playgroud)

要么

mov  DWORD PTR [myVar], eax    ; myVar = eax
Run Code Online (Sandbox Code Playgroud)

这种表示法在NASM等其他汇编程序中必需的,这些汇编程序不是强类型的,并且不记得它myVar是一个DWORD大小的内存位置.

在取消引用寄存器操作数时,根本不需要这个,因为寄存器的名称表示它的大小.al并且ah总是BYTE-sized,ax总是WORD-sized,eax总是DWORD-sized,并且rax总是QWORD-sized.但是,如果你愿意的话,无论如何都要包含它并没有什么坏处,因为它与你记录内存操作数的方式保持一致.


另外,当我尝试这样做时,mov eax, [ebx]我得到一个编译错误,为什么会这样?

嗯......你不应该.在MSVC的内联汇编中,这对我来说很好.正如我们已经看到的,它相当于:

mov  eax, DWORD PTR [ebx]
Run Code Online (Sandbox Code Playgroud)

并且意味着指向的内存位置ebx将被取消引用,并且DWORD将加载-sized值eax.


为什么我不能这样做不mov a, [eax]应该使"a"成为指向eax所指向的地方的指针?

不可以.这种操作数的组合是不允许的.从指令的文档中MOV可以看出,基本上有五种可能性(忽略备用编码和段):

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
Run Code Online (Sandbox Code Playgroud)

请注意,没有mov memory, memory,这是你正在尝试的.

但是,你可以a点到什么eax是简单的编码指向:

mov  DWORD PTR [a], eax
Run Code Online (Sandbox Code Playgroud)

现在aeax拥有相同的价值.如果eax是指针,那么a现在是指向同一内存位置的指针.

如果你想设置a值,eax指向,那么你需要做的:

mov  eax, DWORD PTR [eax]    ; eax = *eax
mov  DWORD PTR [a], eax      ; a   = eax
Run Code Online (Sandbox Code Playgroud)

当然,这会破坏指针并用取消引用的值替换它.如果你不想丢失指针,那么你将不得不使用第二个"临时"寄存器; 就像是:

mov  edx, DWORD PTR [eax]    ; edx = *eax
mov  DWORD PTR [a], edx      ; a   = edx
Run Code Online (Sandbox Code Playgroud)

我意识到这有点令人困惑.该mov指令在x86 ISA中过载了大量潜在含义.这是因为x86作为CISC架构的根源.相比之下,现代RISC架构在分离寄存器寄存器移位,存储器负载和存储器存储方面做得更好.x86将它们全部变成一条mov指令.现在回去修理它已经太晚了; 你只需要熟悉语法,有时需要第二眼.

  • 难以置信,你可以写这么多段落来解释解引用运算符 (5认同)