MOV EBX, [MY_TABLE] 将从地址 MY_TABLE 的内存中读取 4 个字节(即“数组”的前两个元素)。
根据评论,我猜作者打算使用LEA EBX, [MY_TABLE]。
LEA 是“加载有效地址”的缩写,它基本上是指令MOV 的前半部分,带有内存引用变体。它将计算最终地址,这样MOV 将从那里加载数据,但不是联系内存芯片并加载数据,而是将地址本身加载到目标寄存器中。同样可以通过MOV ebx,MY_TABLE 在 NASM 中实现,或者通过MOV ebx,OFFSET MY_TABLE 在 MASM/TASM 中实现。
所以是的,它就像指向表中第一个值的指针,但要充分理解 Assembly 的原始性,您应该记住,它就像指向表中第一个 BYTE 的指针,因为 Assembly 中的指针没有任何类型,在具有平面内存模型(Win32、linux32)的 32b 保护模式下,地址为uint32_t,并且内存可以按字节寻址,因此每个这样的数字都指向内存中的某个位置(物理 RAM 芯片/设备 I/O 映射的位置,或者进入空的无效区域)。这将简单的平面内存 32b 模式限制为 4GiB 的内存空间(x86 允许在 32b 模式下使用更复杂的内存映射方案,这允许寻址超过 4GiB 的内存地址空间,但当然不能使用单个 32b 寄存器地址)。
2) 因为该指针指向 BYTES,并且您的表来自 WORDS,所以每个字的大小为 2 个字节。表的第一个元素位于偏移量0,但也占用了偏移量1 的下一个字节。第二个值的第一个字节(C 中的 table[1])位于偏移量2。因此add ebx,2 用于获取第二个值的地址。 add ebx,1 会让你指向第一个 my_table[0] 值的中间。
尽量不要像 C 对应物那样引用这些东西,数组本身是低级的,所以它经常匹配,但任何更高级别的 C 构造通常只会使它更加混乱。 C 数组已经对您隐藏了指针算法,使用元素类型为您计算正确的地址。在装配中不会发生。
32b x86 如何处理类似 C 的值 my_table[1] 的其他变体:
lea ebx,[my_table]
mov esi,1
mov ax,[ebx + esi*2] ; by index register scaled by 2
lea ebx,[my_table]
mov esi,2
mov ax,[ebx + esi] ; by offset
lea ebx,[my_table]
mov ax,[ebx + 2] ; by immediate offset
mov ax,[my_table + 2] ; by absolute address
编辑:现在我终于注意到了你对 ebx 的实际操作。我认为MOV [EBX], 110“草率”的编程风格,因为从mov 这样的两个论点都不清楚数据大小是多少。
如果你使用mov [ebx],ax,ax 的大小将此类操作的数据宽度定义为 16 位,但110 可以是 8、16 或 32 位立即数(在 64b 模式下甚至可以是 64b)。因此,在这种情况下(内存引用与立即)正确/好的样式是显式指定参数大小,例如:
mov [ebx], word 110 ; C-like: ((short *)my_table)[some_index] = (short)110;
出于现代 x86 的性能原因,最好避免使用寄存器的 16 位部分,因此当您处理短数组或字节数组时,最好通过将它们扩展为完整的 32 位值来加载它们:
movzx eax,word [ebx] ; to extend word value [ebx] with zero bits
movsx ecx,byte [esi] ; to sign-extend value [ebx]
; the top most (sign) bit of original value is copied up to fill upper ecx
然后您可以使用寄存器的 32b 变体(上例中的eax 和ecx)进行所有计算,并仅在计算结束时使用部分ax 和cl 来存储正确的结果(当然要确保计算本身不会因为使用 32 位寄存器/值而产生错误结果)。