我什至不会在大会中使用“变量”这个词。
你当然可以这样想,而且大多数情况下它都会起作用,但从技术上讲,它的级别更低。
var1 DWORD 12345678h 会将值12345678h 编译为四个字节(在 x86 little-endian 上,字节将为78 56 34 12),这些将“降落”到.data 段的某个位置,加载后将成为内存的内容操作系统可执行。操作系统会选择一些空闲内存来加载它,所以它也会提供.data段的起始地址,并在二进制加载完成后调整加载的代码以反映真实地址,然后再执行。
这意味着78 56 34 12 字节将在某个特定地址可用(x86 上的内存可按字节寻址)。
而var1 将成为符号表中的“符号”,标记这四个字节中第一个字节的地址。
然后你可以写成汇编指令,比如mov cl,[var1],意思是“从内存中的符号var1的地址加载cl”,这条指令被标记为可执行,操作系统会用真实的符号来调整它表值,因此在执行期间它将指向加载 .data 段的正确内存。
或者在 x86_64 中使用相对寻址时,mov 指令编译为mov cl,[rip+offset_between_next_instruction_and_var1],则操作系统根本不需要调整指令,因为它可以在任何内存位置工作,偏移量为相对的。
从地址 [var1] 的 BYTE 大小加载内存内容将加载 78h - 非平凡变量思维中断操作的示例。并且从[var1+1] 加载 WORD 将从内存中加载 WORD 值3456h(在非 x86 平台上未对齐的内存访问可能会导致执行崩溃,在 x86 上它会起作用,只是性能受到惩罚)。
那么var1 DWORD 1,2,3,4 只是意味着你在var 地址后面编译了更多的字节值,例如01 00 00 00 02 00 00 00 .... 在这种情况下。您可以通过将它们寻址为mov eax,[var1 + 2*4] 来处理它们 -> 会将var1[2](Java 类数组)值加载到eax,即3。
注意var1 只是内存地址,所以如果你添加正确的偏移量,你实际上可以通过它寻址来自var2 的数据。因此,在 Assembly 中无意覆盖其他变量是多么容易,例如将 DWORD 错误地写入 BYTE 变量就足够了,并且您已经覆盖了变量之外的 3 个字节的内存,可能被其他变量使用了。
还请注意,在手动访问数组时,您必须始终执行索引的 *1、*2、*4、.. !所以你必须时刻注意数组元素的大小。
两种大小的基本幂可以直接编码成x86指令的扩展寻址模式,比如mov eax,[ebx + esi*4 - 44]用“esi-11”索引在ebx地址寻址一些dword数组,所以类似于Java的它类似于@987654347 @,让你不用单独计算乘法。
这是另一个常见的错误来源,仅当数组元素为单字节大小时忘记“索引”等于“内存字节偏移”,在所有其他情况下,您必须将索引乘以元素大小才能获得字节-内存寻址的偏移量。
最后,当您将这些内容写入.data 段时,它们会按顺序编译为连接的字节流(检查您的汇编器规范以了解特定指令的任何自动填充,例如dword,提前插入填充字节根据需要对齐结果数据)。所以你实际上不需要任何var1 那里,如果你想去硬核,自己计算所有偏移量,并通过.data + offset 处理这些字节,这是可能的(这只是向你展示什么意思的心理练习在线上写“更多价值”,而不是建议:))。
edit: "irvine" ...所以您可能正在使用汇编程序 MASM?它不仅在编译期间保留了地址符号,而且还记住了第一个声明大小(如“DWORD”),因此它会尝试使用更“类似变量”的编译方法来覆盖一些用例。
我个人建议您忽略这一点,仅将它们视为地址并避免所有 MASM 怪癖语法,因为 1)它在其他 x86 汇编器中不起作用 2)在较大的源代码中可能会非常混乱,一旦您习惯低级程序集。
我的意思是,在 MASM 中,mov eax,var1 被编译为机器代码 mov eax,[address_of_var1](在 var1 处加载内存内容的 eax,即“从人类的角度来看,“用变量 var1 加载 eax”)。
但是当我在没有可见[] 的情况下读取源指令时,我习惯于认为它不是访问内存并且只使用立即数(例如mov eax,esi 与mov eax,[esi] 的情况)。即使在 MASM 中,您也可以写 mov eax,[var1],它也可以。但是要提取 var1 本身的地址 - 需要额外的语法糖,例如 mov eax,OFFSET var1 IIRC。
最后一点:那些WORD 1,2,3,4 定义通常用于在Java 中使用数组的地方,例如short wordArrayVar[] = {1, 2, 3, 4};。这是对这些多值定义的一种可能解释。但在某些情况下,它甚至在较低级别使用,只是在 .data 段中定义特定的字节值,甚至不用作数组,而是以某种不同的方式使用。
另一种常见的模式是“结构”实例的初始化,在Java中没有很好的例子,因为类成员变量不能保证一个接一个地存储在内存中?但是在 C++ 中,所有用源代码编写的类/结构成员变量都可以想象为逐字节的内存,每个成员变量都有特定的偏移量和对齐方式,由它在源代码中的类型和位置决定。此时,您可以通过在完整结构大小的块中为每个字节定义值来创建此类结构的预初始化实例。