我正在学习Assembly Language For Intel-Based Computers
,有些东西让我很困惑。
众所周知,我们可以使用任何通用寄存器来解决,如下所示:
.data
array1 byte 1,2,3,4
.code
mov esi,offset array1
mov al,[esi] ; al=1
mov al,[esi+1] ; al=2
mov al,[esi+2] ; al=3
mov al,[esi+3] ; al=4
[esi+idata]
(idata是即时数据)就像指针一样强大且足够。
但这本书告诉我如何键入这样的指针:
pbyte tpyedef ptr byte
.data
array1 byte 1,2,3,4
ptr1 pbyte array
.code
mov esi,ptr1
mov al,[esi] ; al=1
mov al,[esi+1] ; al=2
mov al,[esi+2] ; al=3
mov al,[esi+3] ; al=4
因此,真正令我困惑的是,由于array1
是指向数据的指针,ptr1
的重点是什么?指针还需要4个字节来存储。
Isn&t [{1}}是否等于mov esi,ptr1
?
此外,本书给出了不同类型的指针:
mov esi,offset array1
指针只指向内存中的一个字节吗?有什么区别?我试过这样的:
pbyte tpyedef ptr byte
pbyte tpyedef ptr word
pbyte tpyedef ptr dword
pbyte tpyedef ptr qword
我在eax中获得04030201,这是一种dword类型,而不是qword。
总之,我想知道MASM中指针是否存在是非常必要的。或者指针可以有什么东西,但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]
可以&#t; t?
答案 0 :(得分:3)
不是mov esi,ptr1等于mov esi,offset array1?
请注意mov esi, ptr1
实际上意味着mov esi, DWORD PTR [ptr1]
,这是一个重要的区别
这是一个超过mov esi, OFFSET array
的间接级别。
简单地说 - 如果array
位于,例如,0x1000而不是mov esi, OFFSET array
,则只是mov esi, 1000h
的可读版本(执行的实际指令)。
同时,如果ptr1
位于0x1010,则mov esi, ptr1
汇编为mov esi, DWORD PTR [1010h]
- 读取DWORD的内容(0x1010)并将其存储到ESI 。
为了使两个指令具有相同的行为,0x1010处的DWORD必须为0x1000(即指向array
)。
然而,它也可以指向任何其他阵列
在您的示例中,它几乎没用,不是因为您只有一个数组,而是因为数据是静态分配的 - 因此您始终可以使用OFFSET
获取其地址。
想象一下,如果要丢弃字符串的第一个字,过程可以将指针作为参数(如果数据是静态分配的,可以用OFFSET
初始化)并返回指向第一个未丢弃字的指针
+--- Original pointer, initialized with OFFSET
|
v
Hello world from SO!
^
|
+--- Pointer returned
您可以将结果保存到名为ptr1
的变量中
现在,如果要重复此过程,则需要使用ptr1
的值作为过程的输入
否OFFSET
将执行,因为无法在汇编时推断指针的值。
指针不指向内存中的一个字节吗?有什么区别?
一般来说,为汇编程序提供的信息比需要的更多:
知道变量是指针可以帮助未来的读者。
TYPEDEF PTR
can be used to specify near and far pointers,但这与任何现代操作系统设置的保护模式无关。
远指针的初始化与近指针不同。
也许MASM会对指针类型和指针对象进行一些检查?我对此表示怀疑,但我不确定。
正如Michael Petch in the comments所指出的那样:
实际上,MASM确实(与其他汇编程序不同)对已经明确定义然后直接引用的数据的指针进行了基本类型检查。
我在eax中获得04030201,这是一种dword类型,而不是qword。
目标是32位,因此源不能是64位
如果程序员明确使用大小(例如,使用寄存器名称或WORD PTR [...]
),则会覆盖隐式大小。
指针可以但
[esi+idata]
不能吗?
不,任何高级代码功能最终都会组装到ISA指令中,因此通过使用它们,您可以模拟任何高级功能。
请注意,某些指令在元级别工作,例如.data
这些不能被模拟,因为它们不是简单地生成代码。
答案 1 :(得分:1)
在ASM中,您实际上 需要 一个typedef语句来开发应用程序。实际上,它们只不过是评论文本如果你使用FPU需要一些真正的内部格式来学习,但对于标准的32位ASM,你真的只需要知道一些大小的东西,即它是一个字节的字节或者是DWORD。当然,如果你正在与将MSB视为符号位的lib相互作用,或者你需要自己使用有符号值,那么请记住并使用jge代替jae等,但它不是火箭科学。 / p>
大问题: 这本书是否是您需要完成的教育文本,因此会对您坚持其方法的程度进行评分?
如果是,抱歉交配,咬紧牙关并开始记忆那些东西。
如果不是我认为这可能是由在C中思考并且只是将他们以C为中心的思想转化为ASM的人写的。 (不是犯罪 - 可能是从中学习C的好人:)
你显然只是在学习,但你提出的问题非常好。
所以真正令我困惑的是,由于array1是一个指向数据的指针,ptr1的意义是什么?
对于您的示例代码,没有。由于ptr1上的DWORD正在初始化为OFFSET array1,因此从未更改过。 但是当你想要维护一个列表/数组等的全局指针时, 很多次。如果你每次输入代码段时都从数组的开头开始,那么当你指出,你可以使用OFFSET ......简单。但是,如果你的任务的性质是这样的,你必须记住你下次回到它的位置,那么你需要将该位置存储在某个地方;因此指针。
指针还需要4个字节来存储。
当然可以,但是如果指针是正确的工作方式,那么4个字节就不算什么了。如果是我,我只会声明为:
.data
lpThisArray dd OFFSET array1
因为它是什么,它是一个DWORD。它是32个iddy-biddy晶体管,它们可以在任何特定时刻分别举办派对或者休息。他们既不知道也不关心他们是否持有数组指针,光标Y co-ord,你的堂兄弟单元格号,或者单词" 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
他们将编译为相同的可执行文件。
Isn&#t; tt es esi,ptr1等于mov esi,偏移array1?
抛开我个人不喜欢作者不必要的深奥数据大小声明的选择,答案是肯定的,只有当ptr1指向数组的开头时。 ptr1定义了一个可以保存任何32位值的DWORD位置,但OFFSET array1是一个固定值,它是数组中第一个字节的地址。
好的...初学者的OFFSET。好点子。可能最好先考虑&#34; .data&#34;在它之前。
与高级语言中的声明不同,那就是&#34; .data&#34;不是一个方便的以人为中心的标题,您可以在其中列出您想要玩的数字。在ASM中,它定义了exe文件中物理部分的开始,系统加载器将映射到进程中的特定地址。虚拟地址空间。从编译程序开始,其中的所有内容都被定义为位于特定的预定位置。 (理论上它们可以重新基础,但实际上它们几乎从不存在,而且无论如何都与此无关)。
人们习惯于更高级别的语言可以原谅我们假设当程序运行时,它会达到这样的程度:#OF; OFFSET&#34;那家伙,谁迅速逃跑,发现阵列然后报告其位置。事实上,OFFSET是一个编译器指令,它将使编译器将所提供标签的实际内存地址直接硬编码到程序的操作码中。在这种情况下,array1标记数组中第一个字节的位置。那&#34; OFFSET&#34;确切地定义了一个和唯一一个在可执行文件的生命周期内永远不会改变的位置。在这种情况下,您可以将ptr1视为一个变量,因此它可以保存任何32位值,并且该值可以每秒更改1000次。
数据标签 这对于解释指针点很方便,但实际上它是不准确的。 &#34; ptr1的&#34;实际上是一个标签。它标记.data部分中的特定位置,并且在程序的生命周期中也是绝对固定的。它是该位置的4个字节,用于保存指针的值。
例如:
mov ptr1,12345
;( then later)
mov esi,OFFSET ptr1
mov eax,[esi]
; eax now holds 12345
OFFSET可以接受ptr1作为参数意味着必须修复它。数据标签与任何其他标签一样定义位置,差异只是语法约定中的一种,因为数据标签中应用了暗示解除引用。 &#34; ptr1的&#34;就像所有其他标签一样,实际上是OFFSET的结果,因为它编译成一个恒定的32位立即值,但当我们编码&#34; ptr1&#34;时,它是一个数据标签。 MASM编译&#34; [ptr1]&#34;。这使人们更容易将数据标签视为变量并将其用作变量,但它也确实会模糊实际发生的事情。
关于数据标签的另一件事是它们附加了一个大小(不是类型)。 这是MASM的功能,许多人(有时包括我自己)会倾向于错误地将其称为类型检查的形式。虽然此时它几乎变成了在哲学讨论中,MASM确实着名地记录了实际可以确定的所有内容的大小,这使得用户不必键入&#34; DWORD ptr&#34; &#34; BYTE ptr&#34;或者&#34; WORD ptr&#34;对于几乎所有教学而言都是恶心的。这不是必需的,因为编译器已经知道几乎所有内容的大小。
在我看来,这实际上是作为节省时间/提高生产力的功能实现的,但副产品是当编译器尝试处理涉及两种不同大小定义的指令时,它不知道哪一个是正确的,因此不知道它应该生成哪个操作码,因此它不能继续。唯一令我烦恼的是,我觉得我们应该能够通过明确声明一个SIZE ptr来覆盖它,而不必诉诸于使用违规的数据标签加载一个寄存器并取消引用它从而脱落的旧板栗。附加尺寸声明。
指针是否只指向内存中的一个字节?
是的..有点儿。指针只是一个数字,它指向一个地址多个字节,因为使用32位处理器,您可以读取或写入1或2个字节来往/来自该地址的时间。
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个字节。
我得到04030201的eax,dword类型,而不是qword
我恳请你,如果你想弄明白如何将qword变成eax,请告诉我这个诀窍;)