寻址模型就是根据地址获取到数据,通常有三种寻址模型:
正如前面章节讲到的,要获取存储在内存中的值时要使用中括号[],如果漏掉中括号,其实获取到的是地址。如下代码所示:
寄存器模式就是说,操作数是CPU寄存器。下面的代码中,eax和rbx都是寄存器模式
moveax,ebx立即数模式顾名思义,立即数模式就是操作数是一个立即数
内存模式指的是操作数是一个地址,也叫解引用。内存模式最基础的形式,前面已经多次用到了。如下的代码访问qNum变量的地址并获取到里面存储的值:
当访问数组时,有更加通用的办法。特别地,可以将地址放到寄存器、然后使用寄存器来进行解引用。举例说明,假设存在如下的数组。
数组的第一个元素,可以通过如下方式获取:
moveax,dword[lst]也可以通过如下方式:
第二种方法,可以使得我们再访问获取其他数组元素时更为方便。之前讲过,内存地址的计算单元是byte,一个dword占用4个byte,因此第二个元素的地址是首元素地址加上4,后续的每个元素的地址都依次加上偏移4.
byte类型的数组每个元素地址的偏移是1,word类型的数组每个元素地址的偏移是2,dword类型的数组每个元素地址的偏移是4,qword类型的数组每个元素地址的偏移是8.
有多种办法可以获取数组的元素。第一种就是基地址加上偏移。如下的代码示例了获取lst中第三个元素的三种方法:
movrbx,lstmovrsi,8;method1moveax,[lst+8];method2moveax,[rbx+8];methodmoveax,[rbx+rsi]显然,上述的三种方法都不会影响rbx和rsi寄存器本身存储的内容。关于访问数组元素,通用的形式如下:
其中:
如下均是合法的表达式
moveax,dword[dVar1]movrax,qword[rbx+rsi]movax,word[lst+4]movbx,word[lst+rdx+2]movrcx,qword[lst+(rsi*8)]moval,byte[buff-1+rcx]moveax,dword[rbx+(rsi*4)+16]如果要访问前面定义的dword数组的第三个元素,代码如下:
如下代码用于计算一个数组所有元素的和以及平均数。
如下代码用于计算一系列四棱锥的表面积和体积,并找出最大、最小、和、平均值。
两个收获:
movecx,dword[length];lengthcountermovrsi,0;indexcalculationLoop:movzxr8d,byte[bLst+rsi]movzxr9d,word[wLst+rsi*2];getvalueaccordingoffset...incrsiloopcalculationLoopcmpeax,dword[taMin]jaenotNewTaMinmovdword[taMin],eaxnotNewTaMin:cmpeax,dword[taMax]jbenotNewTaMaxmovdword[taMax],eaxnotNewTaMax:课后习题1.解释如下两条指令的不同:
movrdx,qword[qVar1]movrdx,qVar1解答:第一条指令用于取值,取qVar1的值;第二条指令用于取地址,取qVar1的地址2.下面的代码语句中,源操作数使用的寻址模型是什么?(寄存器/立即数/内存/非法语言/)解答:
3.如下代码在执行完之后,eax寄存器的内容。
解答:0x000000000000000A
4.如下代码在执行完之后,eax和edx寄存器的内容
list1dd2,3,4,5,6,7movrbx,list1addrbx,4moveax,dword[rbx]movedx,dword[list1]解答:
5.如下代码执行完之后,eaxebxrcxrsi寄存器的内容
解答:
listdd8,6,4,2,1,0movrbx,listmovrsi,1movrcx,3movedx,dword[rbx]lp:moveax,dword[list+rsi*4]incrsilooplpimuldword[list]解答:
8.如下代码执行完之后eaxedxrcxrsi寄存器的内容:
listdd2,7,4,5,6,3movrbx,listmovrsi,1movrcx,2moveax,0movedx,dword[rbx+4]lp:addeax,dword[rbx+rsi*4]addrsi,2looplpimuldword[rbx]解答:
1.实现示例程序,计算list所有元素之和,通过调试验证。
解答:略,参考前面代码
2.更新代码,计算list中的最大值,最小值和平均值,通过调试验证。
3.完成示例程序,计算四棱锥的表面积和体积
4.使用冒泡排序,对list进行排序
for(i=(len-1)to0){swapped=falsefor(j=0toi-1)if(lst(j)>lst(j+1)){tmp=lst(j)lst(j)=lst(j+1)lst(j+1)=tmpswapped=true}if(swapped=false)exit}解答:
执行结果:
程序员要去杂货铺,妻子告诉他,买2瓶牛奶,如果碰到卖鸡蛋的,买一打。最后买了12瓶牛奶回家
在计算机中,栈是一种非常有用的数据结构,满足LIFO即后进先出的要求,具备2个基础的操作:push、pop
指令基本形式如下:
栈的实现是通过调整rsp实现的。
入栈push:
出栈:
如下代码将数组进行颠倒.
1.哪一个寄存器指向当前的栈顶?
解答:rsp
2.执行pushrax是发生了什么?
3.poprax将会从栈移走多少byte的数据?
解答:8
4.如下片段代码执行完之后,r10,r11,r12的内容各是什么?
movr10,1movr11,2movr12,3pushr10pushr11pushr12popr10popr11popr12解答:
5.如下代码片段的执行效果是什么?
解答:颠倒数组,并获取数组的首元素(原最后一个元素)
解答:堆和栈相向生长,有助于更加充分的使用整个内存空间。
1.实现一个程序,判断字符串是不是一个回文字符串,可以使用Stack实现
2.实现一个程序,判断一个段落是不是回文字符串,忽略掉逗号、破折号和感叹号。
通用程序开发的几个过程:
本文以一个具体的例子,展示程序开发的全流程
设计一个程序,将数值转化为字符串类型。简化起见,仅考虑dword类型的无符号数
所谓算法,就是为了解决一个问题,需要进行的具体的操作步骤
算法的伪代码如下:
解答:算法就是解决一个具体的问题需要的步骤
2.需求开发通用的4个步骤
3.4个步骤是否只适用于汇编语言程序开发?
解答:否,是通用的
4.如果针对乘法指令,操作数出现立即数,会触发编译时异常还是运行时异常?
解答:编译时异常
5.如果汇编指令出现拼写错误,会触发什么异常?
6.如果一个Label被引用到,但是却没有定义,会触发什么异常?
7.如果在一系除法操作中出现除0错误,会触发什么异常?
解答:运行时异常
1.修改示例程序,要求对有符号数进行转化
解答:中间遇到了不少坑,一开始想使用有符号数的除法,但是忘记正确设置rdx的方法(这种场景应该使用cdq)、修改OK后有发现有符号数的除法对于余数的处理有点反常,最终对负数采用了neg命令取反后按照正数处理了。
3.编写一个程序,将字符串的数字转化成数值,处理有符号数的情况
汇编语言具备非常强大的宏的预处理能力,支持条件宏、多级文件包含、单行和多行以及上下文栈能力。
在使用宏之前,宏必须要先被定义,宏需要在data段和text段之前定义,在text段使用。
使用%define指令定义单行宏。例如
定义好之后就可以按照如下方式使用:
mulby4(rax)多行宏按照如下方式定义:
需要注意的是,对应的参数需要使用%
同时,如果要在宏中使用label,必须使用%%前缀。
%macroabs1cmp%1,0jge%%doneneg%1%%done:%endmacro宏的使用举例下面的例子展示了上述的abs宏如何使用:
我们设计一个宏,它可以求助数组的平均值。
;ExampleProgramtodemonstrateasimplemacro;@BianChengFeiChai;**************************************************;Definethemacro;calledwiththreearguments:;aver
1.宏定义的代码放置在什么位置?
解答:源代码最前面的位置
2.如果宏被调用,它的代码会在被展开替换多少次?
解答:每一次的调用,都会产生宏代码替换展开。
3.解释下,为什么在宏中的Label必须要用%%符号?
4.解释如果在宏中使用的Label不携带%%会发生什么?
5.在宏中跳转到一个不包含%%的label是否是合法的,为什么?
解答:合法的,宏无非就是代码替换。有些场景是可以这么使用的,例如,对于异常的控制、检测到异常后都统一跳到last标签。
解答:编译阶段
1.更新示例代码,在求取平均数的同时,包括最小值和最大值
2.创建一个程序,创建一个宏将数组中的每个元素乘以2
解答:本题遇到了两个坑,一个是一开始出现了寄存器使用冲突,明明rbx寄存器已经用来存储数组的基地址了、自己还用ebx寄存器存储imul的中间结果;还有一个坑是,imul三个操作数的场景,目的操作数不能直接是内存地址,但是这个限制书中也没有提及。
在linux上,gdb默认使用ATT格式。可以使用如下的命令查询&设置