Assembly 为什么在NASM中,我们必须使用方括号([])来移动到内存位置?

Assembly 为什么在NASM中,我们必须使用方括号([])来移动到内存位置?,assembly,nasm,yasm,Assembly,Nasm,Yasm,例如,如果我有一个名为test的变量,声明如下: test db 0x01 ;suppose the address is 0x00000052 如果我这样做: mov rax, test ;rax = 0x00000052 mov rax, [test] ;rax = 0x01 但是,当我尝试在其中保存时,如果我们遵循相同的模式: mov test, 0x01 ;address 0x00000052 = 0x01 mov [test], 0x01 ;addre

例如,如果我有一个名为test的变量,声明如下:

test db 0x01      ;suppose the address is 0x00000052
如果我这样做:

mov rax, test     ;rax = 0x00000052
mov rax, [test]   ;rax = 0x01
但是,当我尝试在其中保存时,如果我们遵循相同的模式:

mov test, 0x01    ;address 0x00000052 = 0x01
mov [test], 0x01  ;address 0x01 = 0x01
但实际上是:

mov [test], 0x01  ;address 0x00000052 = 0x01

那么,为什么方括号的行为会因它们是第一个操作数还是第二个操作数而有所不同呢?

在大多数汇编程序中,使用方括号会取消对内存位置的引用。您将该值视为内存地址

例如,让我们以这个为例

mov ax, [0x1000]
这将获取0x1000处的值并将其放入AX。如果删除方括号,则仅移动0x1000

如果将一个值移动到一个数字,则将其放入该值(内存位置)

如果您是C开发人员,下面是一个示例问题。 如果你被其他人欺负学习C语言,称你为“巨魔”,不要让这个例子激怒你

如果愿意,您可以忽略此项,但如果您了解C,您可能已经了解了
scanf()

int a = 10;
scanf("%d", a);
这是一个非常常见的错误,因为我们没有得到变量的内存地址。相反,我们使用它的值作为地址。
scanf()
函数要求您提供地址

如果我们这样做

scanf("%d", &a);

我们会得到变量a的地址。

史蒂夫·伍兹的帖子给我的印象是他认为是一个解引用操作符。&是C的参考运算符。*是C的解引用运算符。OP有一个合理的担忧。[]似乎可以根据上下文同时起作用。它既不是解引用运算符也不是引用运算符。它是“这是一个内存地址!!!”操作符

有效地址是引用内存的指令的任何操作数。在NASM中,有效地址有一个非常简单的语法:它们由一个表达式组成,该表达式计算所需的地址,并用方括号括起来

当操作数是内存地址时,必须将其括在方括号内,以告知nasm情况就是这样。这不是去引用它,它只是让纳斯姆知道发生了什么。如果它等价于解引用运算符

mov [wordvar], eax
将内存位置12设置为13


它不是解引用操作符。这是“这是一个内存地址”操作符。在不同的情况下,这似乎是解引用和引用,因为x86和x86_64指令的行为因其操作数是内存位置还是内存值而异。我在自学汇编,我必须自己解释这个问题。mov test,0x01意味着
0x00000052=0x01
,即number=other_number,这没有意义。您的注释“address 0x00000052=0x01”以某种方式假定值0x52是内存地址,但没有理由这样假定。顺便说一句,
test
不是变量,它是特定内存地址的符号标签
0x52
,您可以通过
test:
创建标签,您不需要使用
db
指令来保留任何空间(尽管您应该这样做,如果您想覆盖标签后面的字节)。我的争论是关于你是怎么想的,asm中没有变量,
mov[test],0x01;地址0x01=0x01
也有奇怪的注释。。。它是
mov[0x52],1
=将值
1
存储到地址
0x52
处的内存中,并且它是不明确的,因为汇编程序无法从该源代码中判断是否要存储8/16/32/64位值
1
,NASM应该失败或者至少在该行发出警告。在不明确的情况下,您应该明确指定大小,如
mov byte[test],1
->,以便只将单个字节写入内存。(顺便说一句“为什么”-因为Intel语法用方括号标记内存访问,NASM创建者决定严格遵循这一点);x=a vs y=*a在这个asm语法中,后者带括号,前者不带括号。在x86 asm中,目标始终是第一个操作数
mov-rax,[test]
是一个加载,另一个顺序是一个存储(不同的操作码但相同的助记符)。在加载/存储体系结构时,它们使用单独的助记符,如
lw
sw
,通常不遵循哪个操作数是ALU指令的目标的模式。e、 g.MIPS
lw$t0、($a0)
sw$t0、($a0)
,而不是
sw$a0、$t0
。但在x86上,几乎所有指令都可以有一个内存源或内存目标,因此它们总是遵守操作数顺序。关键是MASM是一个奇怪的/不一致的指令,它使
mov eax,symbol
成为一个负载,即使它没有括号。要确定它是一个负载还是一个mov立即数,您必须查看它是定义为
eq
还是
=
常量还是标签。NASM强制你使用与你定义名称的方式相匹配的语法,这样你就可以随时知道它是什么类型的指令。C假设所有变量都有一个地址,使用它们的裸名称可以得到该内存的内容,而不是地址。因此,如果您想在C中输入地址,您需要添加一个
&
,而在NASM中,您需要省略
[]
。在NASM中,裸名称类似于数字常量或指针常量,类似于C
static const*a=some_static_address
您确实需要
*a
来引用内存的位置。或对于等式常数,
static const*a=12345因此,无论如何,
[]
始终取消对符号值的引用以访问该位置的内存。(从技术上讲,符号值是它们的地址,而不是指向内存的地址。将标签放在某个位置与
foo eq 0x401000
非常相似,只要您稍后在
[]
内部或外部使用该标记时会发生什么情况)。因为我们知道x86没有内存间接寻址,
[foo]
不可能是从内存加载地址然后取消引用地址的语法。
mov [wordvar], eax