在 ASM 中,您实际上永远需要 typedef 语句来开发应用程序。实际上,它们只不过是注释文本。如果您使用 FPU,则需要学习一些真正的内部格式,但对于标准 32 位 ASM,您真的只需要知道某些内容的大小,即它是 BYTE、WORD 还是 DWORD。当然,如果您正在与将 MSB 视为符号位的库进行交互,或者您需要自己使用有符号值,请记住这一点并使用 jge 而不是 jae 等,但这不是火箭科学。
一个大问题: 这本书是一本你需要通读的教育书籍,因此将根据你对方法的坚持程度进行评分吗? p>
您显然只是在学习,但您提出的问题非常好。
所以真正让我困惑的是,既然array1 是一个指向数据的指针,那么ptr1 的意义何在?
对于您的示例代码,没有。由于 ptr1 处的 DWORD 被初始化为 OFFSET array1 然后永远不会改变。
然而, 很多时候你会想要维护一个指向列表/数组等的全局指针。如果你每次输入那段代码时都从数组的开头开始,那么当你指出,你可以使用 OFFSET ... 简单。但是,如果您的任务的性质是,您必须记住下次返回时要到达的位置,那么您需要将该位置存储在某个地方;因此指针。
指针也需要4个字节来存储。
当然可以,但是不要太拘泥于指针空间,如果指针是完成这项工作的正确方法,那么 4 个字节就不算什么。如果是我,我会声明为:
.data
lpThisArray dd OFFSET array1
因为它就是这样,它是一个 DWORD。它是 32 个 iddy-biddy 晶体管,它们在任何给定的时刻单独地要么开派对,要么睡着。他们既不知道也不关心他们是否持有数组指针、光标 Y 坐标、您的堂兄弟单元格编号或单词“TWIT”。他们有生活要过,他们不关心我们的问题……但没关系!我们在这里不是盲目的。我们称它为“lpThisArray” 我非常有信心我不会不小心拨通它并试图让他在下班途中接我一些东西。我在家也一样聪明,我几乎总是记得不要把餐具插在电源插座上——当你全神贯注的时候,你能做的事情真是太棒了!
在 32 位域中,几乎不需要使用 db、dw 或 dd(= BYTE、WORD 或 DWORD)以外的任何东西来声明标准数字/字符串/指针等内容,因为最终这就是一切无论如何。除了 FPU/SSE 等之外,一切都是 8 位、16 位或 32 位。
当然,结构定义和语法非常方便,因此如果您正在使用 API 结构(或自己制作大型结构),使用它们是有意义的。
MyOfn OPENFILENAME
反正打字很容易;)
...只要你明白:
.data?
csrPos POINT <>
.code
invoke GetCursorPos,OFFSET csrPos
mov ebx,csrPos.y
在各方面都相同:
.data?
csrX dd ?
csrY dd ?
.code
push OFFSET csrX
call GetCursorPos
mov ebx,csrY
它们将编译为相同的可执行文件。
mov esi,ptr1 不等于 mov esi,offset array1 吗?
撇开我个人不喜欢作者不必要地深奥的数据大小声明选择,答案是肯定的,只有当 ptr1 指向数组的开头时。 ptr1 定义了一个 DWORD 位置,可以保存任何 32 位值,但 OFFSET array1 是一个固定值,它是数组中第一个字节的地址。
好的......初学者的偏移量。好点子。最好先考虑一下它之前的“.data”。
与高级语言中的声明不同,“.data”不是一个方便的以人为中心的标题,您可以在其中列出您想要使用的数字。在 ASM 中,它定义了 exe 文件中物理部分的开始,系统加载程序将映射到进程虚拟地址空间中的特定地址。从程序编译的那一刻起,其中的所有内容都被定义为位于特定的预定位置。 (理论上它们可以重新建立,但实际上它们几乎从来没有,而且无论如何这与此无关)。
习惯于高级语言的人可以原谅假设程序运行时,它会找到这个“OFFSET”人,这个人会迅速跑开并找到数组,然后报告它的位置。事实上,OFFSET 是一个编译器指令,它将导致编译器将提供的标签的实际内存地址直接硬编码到程序的操作码中。在这种情况下,array1 标记了数组中第一个字节的位置。该“偏移量”准确地定义了一个且唯一的位置,该位置在可执行文件的生命周期内永远不会改变。在这种情况下,您可以将 ptr1 视为一个变量,因此它可以保存任何 32 位值,并且该值每秒可以更改 1000 次。
数据标签
这对于解释指针点很方便,但实际上它是不准确的。 “ptr1”实际上是一个标签。它标记 .data 部分中的特定位置,并且在程序的生命周期内也是绝对固定的。保存指针值的是该位置的 4 个字节。
例如:
mov ptr1,12345
;( then later)
mov esi,OFFSET ptr1
mov eax,[esi]
; eax now holds 12345
OFFSET 可以接受 ptr1 作为参数这一事实意味着它必须被修复。数据标签像任何其他标签一样定义位置,区别只是语法约定之一,因为数据标签隐含取消引用应用于它们。 “ptr1”就像所有其他标签一样,实际上是 OFFSET 的结果,因为它编译为一个恒定的 32 位立即值,但作为一个数据标签,当我们编码“ptr1”时,MASM 编译“[ptr1]”。这使人们更容易将数据标签视为变量并将其作为变量来使用,但它也往往会掩盖实际发生的情况。
关于数据标签的另一件事是它们附加了一个大小(不是类型)。 这是 MASM 的功能,许多人(有时包括我自己;)倾向于错误地将其称为类型检查的一种形式。 虽然在这一点上它几乎变成了一种在哲学讨论中,MASM 确实记录了可以实际确定的所有内容的大小,这使用户不必为几乎每条指令键入“DWORD ptr”、“BYTE ptr”或“WORD ptr”而令人作呕。这不是必需的,因为编译器已经知道几乎所有东西的大小。
在我看来,这在很大程度上是作为一种节省时间/提高生产力的功能来实现的,但是副产品是,当编译器尝试处理涉及两种不同大小定义的指令时,它不知道哪一个是正确的,因此不知道应该生成哪个操作码,因此无法继续。唯一让我感到厌烦的是,我觉得我们应该能够通过显式声明 SIZE ptr 来覆盖它,而不必诉诸于旧栗子,即用有问题的数据标签的偏移量加载寄存器并取消引用它,从而摆脱附上尺寸声明。
指针不只是指向内存中的一个字节吗?
是的..有点。指针只是一个数字,它指向一个 address 多于一个 byte,因为使用 32 位处理器,您可以一次读取或写入 1 2 或 4 个字节往返该地址的时间。
pt typedef ptr qword
.data
array byte 1,2,3,4,5,6,7,8
arrptr pt array
.code
mov esi,arrptr
mov eax,[esi]
这只是由于过度消费我们将成为普利策奖得主的产品而导致的一点混乱......
实际上你已经这样做了:
.data
array db 1,2,3,4,5,6,7,8
arrptr dd OFFSET array
.code
mov esi,arrptr
mov eax,[esi]
这是一种相当迂回的方式:
.data
array db 1,2,3,4,5,6,7,8
.code
mov eax,OFFSET array
mov eax,[eax]
由于您已将一个值加载到源是取消引用的指针的 eax 中,因此可能已附加到先前保存该指针的容器的任何大小定义现在早已不复存在。对于这个 mov 指令,数据大小由目标操作数定义,即 eax 本身。由于 eax 是一个 32 位(4 字节)的寄存器,因此已经加载了 4 个字节。
我在 eax 中得到 04030201,一个 dword 类型,而不是一个 qword
我恳求你,如果你知道如何将一个 qword 输入 eax,请告诉我诀窍;)