学win32汇编[13]:定义符号常量(=、equ、textequ)
汇编中.equ的作用

虽然数据段主要用于定义变量数据但是也可以在这里声明静态数据符号
汇编中Байду номын сангаас.equ的作用
定义静态符号
虽然数据段主要用于定义变量数据,但是也可以在这里声明静态数据符号。 .equ 命令用于把常量值设置为可以在文本段中使用的符号
如: .equ factor, 3 .equ LINUX_SYS_CALL, 0x80
经过设置之后,数据符号值是不能在 程序中改动的。 .equ 命令可以出现在数据段中任何位置,但是出于好的代码习惯, 最好是在定义其他数据之前或之后集中定义所有数据符号
为了引用静态数据元素,必须在标签名称前面使用美元符号($)。
如: movl $LINUX_SYS_CALL, %eax
把赋值给LINUX_SYS_CALL符号的值传送给EAX寄存器
符号常量的定义方法

符号常量的定义方法符号常量是在程序中用来表示固定数值或者固定字符串的标识符,其数值或字符串在程序运行过程中不会改变。
在许多编程语言中,符号常量的定义方法都是非常重要的,因为它可以提高代码的可读性和可维护性。
下面我们将介绍一些常见的符号常量的定义方法。
首先,我们来看一下在C语言中如何定义符号常量。
在C语言中,可以通过使用`#define`指令来定义符号常量。
例如,我们可以这样定义一个表示圆周率的符号常量:```c。
#define PI 3.1415926。
```。
在上面的代码中,`#define`指令告诉编译器,每当它在代码中遇到`PI`这个标识符时,都应该将其替换为`3.1415926`。
这样,我们在程序中使用`PI`这个符号常量时,就可以直接使用`3.1415926`来代替,从而提高了代码的可读性。
除了使用`#define`指令之外,在C语言中还可以使用`const`关键字来定义符号常量。
例如:```c。
const int MAX_LENGTH = 100;```。
在上面的代码中,我们使用`const`关键字定义了一个整型的符号常量`MAX_LENGTH`,并将其初始化为`100`。
使用`const`关键字定义符号常量的好处是,它可以提供类型安全性,同时也可以让编译器对符号常量进行更严格的检查,从而避免一些潜在的错误。
接下来,让我们看一下在Java语言中如何定义符号常量。
在Java语言中,可以使用`final`关键字来定义符号常量。
例如:```java。
public class Constants {。
public static final double PI = 3.1415926;public static final int MAX_LENGTH = 100;}。
```。
在上面的代码中,我们使用`final`关键字定义了两个符号常量`PI`和`MAX_LENGTH`。
这样,我们在程序的其他地方就可以直接通过`Constants.PI`和`Constants.MAX_LENGTH`来引用这两个符号常量,从而提高了代码的可维护性。
Win32汇编-基本知识总结

Win32汇编-基本知识总结汇编语⾔是所有程序设计语⾔中最古⽼的,它与计算机机器语⾔最为接近,通过汇编语⾔可以直接访问计算机的硬件,能够直接与CPU对话,可以说汇编语⾔是所有编程语⾔中语法格式最⾃由的,但⾃由的代价就是需要了解计算机体系结构和操作系统的⼤量细节,每编写⼀段程序都需要考虑各种硬件的状态,从⽽导致使⽤汇编写程序效率⾮常低.本篇⽂章,⽂字描述部分参考⾃《Intel 汇编语⾔程序设计》这本书,学习后总结的重要笔记,如果时间充⾜可以去阅读此书。
⾃1946年第⼀台计算机问世以来,在短短的60多年中,已经历了由电⼦管计算机(1946年),晶体管计算机(1956年),集成电路计算机(1958年),超⼤规模集成电路计算机(1972年),这五代的更替,⽽且还在不断地向巨型化,微型化,⽹络化,智能化这四个⽅向不断发展.从当今的X86架构的CPU说起,X86指令集是Intel为其第⼀块16位CPU(80x86)专门开发的,IBM公司1981年推出的世界第⼀台PC机中的CPU—i8088(i8086简化版)使⽤的也是X86指令,同时电脑中为提⾼浮点数据处理能⼒⽽增加的X87芯⽚系列协处理器则另外使⽤X87指令,为了提⾼处理器性能,就将X86指令集和X87指令集统称为X86指令集.虽然随着CPU技术的不断发展,Intel公司陆续研制出更新型的i80386、i80486、Pentium直到今天,但为了保证电脑能继续运⾏以往开发的各类应⽤程序以保护和继承丰富的软件资源,所以Intel公司所⽣产的所有CPU仍然继续使⽤X86指令集,所以它的CPU仍属于X86系列,由于X86系列及其兼容CPU都使⽤X86指令集,所以就形成了今天庞⼤的X86系列及兼容CPU阵容.谈完了处理器的基本发展过程,再来了解⼀下CPU指令集的分类吧.处理器分为两⼤架构阵营,即RISC(精简指令集计算机)和CISC(复杂指令集计算机)是当前CPU的两种架构,它们的区别在于不同的CPU设计理念和⽅法,CPU 架构是⼚商给属于同⼀系列的CPU产品定的⼀个规范,主要⽬的是为了区分不同类型CPU的重要标⽰.早期的CPU全部是CISC架构,它的设计⽬的是要⽤最少的机器语⾔指令来完成所需的计算任务.⽐如对于乘法运算,在CISC架构的CPU上,您可能只需要⼀条指令就可以得到相应的结果,这些幕后的操作全部依赖于CPU中设计的逻辑电路来完成,这种架构会增加CPU结构的复杂性和对CPU制作⼯艺的要求,但对于编译器的开发却⼗分有利.相⽐CISC架构的系统,RISC架构则要求软件来指定各个操作步骤,上⾯的乘法运算如果要在RISC架构上实现,则你需要具体指定其特定的实现步骤,使⽤这种架构⽣产CPU,可以很⼤程度上降低CPU的复杂性以及允许在同样的⼯艺⽔平下⽣产出功能更强⼤的CPU,但对于编译器的设计有更⾼的要求.总结:当精简指令集出现后,所有⼈都说复杂指令集已经过时,英特尔密切关注,为了谨慎.英特尔同时开发复杂指令集CPU和精简指令集CPU.精简指令处理器上市后,复杂指令集CPU依旧热销.⽽精简指令集CPU因为⽆法兼容以前的软件,⽽销售量不好.英特尔得出复杂指令集⽣命依旧强⼤的结论,放弃在精简指令集⽅⾯的开发⼯作.80x86处理器的⼏种基本⼯作模式IA-32处理器有三种基本的⼯作模式:实地址模式,系统管理模式,保护模式,另外还有⼀种模式称为虚拟80x86模式,其实虚拟x86模式也是保护模式的⼀个特例,下⾯个将分别简要描述这⼏种系统模式:实地址模式: 在该模式下,IA-32处理器使⽤20位地址线,可以访问1048576(1MB)字节的内存,其地址范围是0-FFFFF,但8086处理器的寄存器是16位的不能存放20位的地址,为了解决这个棘⼿的问题提出了⼀种称为分段内存的概念,所有内存被分为了多个64kb的区域,这些区域称为段(segment),我们使⽤段地址x16+偏移地址=绝对地址来计算出绝对地址.保护模式: 在该模式下,每个程序可寻址4GB的内存,地址范围是0-FFFFFFFF,在该模式下编程⽆需进⾏复杂的公式计算,只需要使⽤⼀个32位整数就可以存放任何指令和变量的地址,处理器会在后台进⾏地址的计算和转换,这些⼯作对于汇编程序员变得透明了起来,保护模式下有三个段:CS:代码段,DS:数据段,SS:堆栈段,其他的段操作系统负责维护.虚拟x86模式: 在该模式下,实际上是处理器在保护模式下创建的⼀个具有1MB地址空间的虚拟机,虚拟机对运⾏于实地址模式下的80x86计算机进⾏了模拟,在Windows NT系统下,打开⼀个控制台窗⼝,就创建了⼀个8086虚拟机,当然你也可同时打开多个控制台,他们之间是隔离的并不互相影响.平坦分段模式: 在该模式下,所有段都被映射到32位的物理地址空间中,⼀个程序⾄少需要2个段:代码段(CS,数据段(DS),每个段都由⼀个段描述符定义,段描述符通常是⼀个存放在全局描述符表(GDT)中的⼀个64位地址.内存分页机制: IA-32处理器⽀持⼀种称为分页(paging)的特性,允许⼀个段被分割成称为页(page)的4096字节的内存块,分页机制允许同时运⾏的程序使⽤总内存远⼤于计算机的物理内存,操作系统映射的所有页的集合称为虚拟内存,操作系统通常都会包含⼀个虚拟内存管理器的程序,分页机制会使⼈产⽣内存⽆限⼤的错觉,然⽽程序如果过度依赖于分页的话,其运⾏效率会⾮常低下.CPU内部的寄存器组,以及每个寄存器的作⽤寄存器是CPU内部的⾼速存储单元,由于是固化在CPU内部的组件,其访问速度快于内存,在当下的处理器中寄存器分为⼏种类型,其中8个通⽤寄存器(EAX,EBX,ECX,EDX,EBP,ESP,ESI,EDI),6个段寄存器(CS,SS,DS,ES,FS,GS),⼀个处理器状态标志寄存器(EFLAGS),和⼀个指令指针寄存器(EIP)寄存器.通⽤寄存器: CPU内部有8个通⽤寄存器主要⽤于算数运算和数据的传送,这8个寄存器都可以作为⼀个32位的值或两个16位的值来寻址使⽤,还可以按照8位寄存器来使⽤,⽐如通⽤寄存器都可以被拆分为⾼低寄存器来存储数据,例如:EAX寄存器,可被拆分为(AX)16位寄存器来使⽤,⽽(AX)16位寄存器还可拆分为AH/AL(⾼低8位).变址寄存器: CPU内部有2个通⽤寄存器ESI和EDI,寄存器ESI、EDI称为变址寄存器(Index Register),它们主要⽤于存放存储单元在段内的偏移量,⽤它可实现多种存储器操作数的寻址⽅式,为以不同的地址形式访问存储单元提供⽅便.变址寄存器不可分割成8位寄存器,在字符串操作指令的执⾏过程中,对它们有特定的要求,⽽且还具有特殊的功能,该寄存器默认和DS数据段寄存器相关联.堆栈指针寄存器: CPU内部有2个通⽤寄存器EBP和ESP,寄存器EBP、ESP称为指针寄存器(Pointer Register),主要⽤于存放堆栈内存储单元的偏移量,它们主要⽤于访问堆栈内的存储单元并且规定,EBP为基址指针寄存器,ESP为堆栈指针寄存器,指针寄存器不可分割成8位寄存器,该寄存器默认和SS堆栈段寄存器相关联.指令指针寄存器: CPU内部有1个指令指针寄存器EIP,该寄存器存放下⼀条要执⾏的指令的地址,下次要执⾏的指令通常已被预取到指令队列中,除⾮发⽣转移情况,所以在理解它们的功能时,不考虑存在指令队列的情况,默认情况下EIP不可⼿动修改,⼀般都是由特殊的指令CALL,RET,PUSH等间接性的修改.段寄存器: 段寄存器是根据内存分段的管理模式⽽设置的,内存单元的物理地址由段寄存器的值和⼀个偏移量组合⽽成的,这样可⽤两个较少位数的值组合成⼀个可访问较⼤物理空间的内存地址,常规段寄存器包括CS:代码段寄存器,DS:数据段寄存器,SS:堆栈段寄存器,ES:附加数据段寄存器这些寄存器通常是由编译器或这是操作系统来维护的.标志寄存器: 标志寄存器(EFLAGS),该寄存器⽤来控制CPU的操作流程,或者反应CPU某些运算的结果的独⽴⼆进制位构成,常⽤的标志位包括CF(进位标志),ZF(零标志),PF(奇偶标志)等.⼿动编译⼀段⼩程序.386p.model flat,stdcalloption casemap:noneinclude windows.incinclude kernel32.incincludelib kernel32.libMyDef equ 1024 ; 将数值指定名称.dataMain WORD 1024 ; 定义可赋值的变量.data? ; 定义未知初始变量lyshark DWORD ?.codemain PROCxor eax,eaxinvoke ExitProcess,0main ENDPEND mainMASM 定义了多种内部数据类型,每种数据类型都描述了该类型的变量和表达式的取值集合,汇编语⾔中数据类型的基本特征是以数据位数为度量单位:8,16,32,48,64,80位,⽽除此之外其他的特征如(符号,指针,浮点数)主要是为了⽅便我们记忆变量中存储的数据类型.接下来看下表,表中是IEEE委员会发布的标准内部数据类型:数据类型作⽤(⽆符号)数据类型作⽤(有符号)BYTE8位⽆符号整数SBYTE8位有符号整数WORD16位⽆符号整数SWORD16位有符号整数DWORD32位⽆符号整数SWORD32位有符号整数FWORD48位整数(远指针)QWORD64位整数定义REAL432位(4字节)短实数REAL864位(8字节)长实数数据类型定义语句为变量在内存中保留存储空间,并且可以选择为变量指定⼀个名字,在汇编语⾔中所有的数据⽆⾮就是BYTE的集合,数据的定义语句格式如下:[变量名] 数据定义伪指令初始值[....]在数据定义语句中使⽤BYTE(定义字节)和SBYTE(定义有符号字节)伪指令,可以为每⼀个或多个有符号或⽆符号字节分配存储空间,每个初始值必须是8位整数表达式或字符常量,例如下⾯的定义:.datavar1 BYTE 'A' ; 定义字符常量var2 BYTE ? ; 定义未初始化变量var3 BYTE 0 ; 最⼩的⽆符号字节常量var4 BYTE 255 ; 最⼤的⽆符号字节常量var5 SBYTE -128 ; 最⼩的有符号字节常量var6 SBYTE +127 ; 最⼤的有符号字节常量如果⼀条数据定义语句中有多个初始值,那么标号仅仅代表第⼀个初始值的偏移,如下我们⾸先定义⼀个BYTE数组,然后通过反汇编查看地址的偏移变化就能看到效果啦:.datalist BYTE 10,20,30,40,5000E71000 | B8 0030E700 | mov eax,main.E73000 | E73000=1000E71005 | B8 0130E700 | mov eax,main.E73001 | E73001=2000E7100A | B8 0230E700 | mov eax,main.E73002 | E73002=3000E7100F | B8 0330E700 | mov eax,main.E73003 | E73003=4000E71014 | B8 0430E700 | mov eax,main.E73004 | E73004=50并⾮所有的数据定义都需要标号,如果想继续定义以list开始的字节数组,可以在随后的⾏上接着上⾯的定义:.datalist BYTE 10,20,30,40,50list BYTE 60,70,80,90,100当然除了定义整数字符以外,还可以定义字符串,要想定义字符串应将⼀组字符⽤单引号或双引号括起来.最常见的字符串是以空格结尾0h,在C/C++,JAVA中定义字符串⽆需添加结尾0h,这是因为编译器会在编译的时候⾃动的在字符串后⾯填充了0h,在汇编语⾔中我们需要⼿动添加字符串结尾的标志:.datastring1 BYTE "hello lyshark",0hstring2 BYTE "good night",0h00F23000 68 65 6C 6C 6F 20 6C 79 73 68 61 72 6B 00 67 6F hello lyshark.go00F23010 6F 64 20 6E 69 67 68 74 00 00 00 00 00 00 00 00 od night........字符串也可以占⽤多⾏,⽽⽆须为每⾏都提供⼀个编号,如下代码也是合法的:.datastring1 BYTE "welcom to the Demo program"BYTE "created by lyshark",0dh,0ah,BYTE "url:lyshark"BYTE "send me a copy",0dh,0ah,0⼗六进制0dh,0ah也称为CR/LF(回车换⾏符),或者是⾏结束的字符,在向标准输出设备上写的时候,回车换⾏符可以将光标移动到下⼀⾏的开头位置,从⽽继续填充新的字符串.有时我们需要初始化⼀些空值的内存空间,在为内存地址分配空间的时候,DUP伪指令就显得尤为重要,初始化和未初始化数据均可使⽤DUP指令定义,其定义语法如下:.datastring1 BYTE 20 DUP(0) ; 分配20字节,全部填充0BYTE 20 DUP(?) ; 分配20字节,且未初始化BYTE 50 DUP("stack") ; 分配50字节,"stackstack...".datasmallArray DOWRD 10 DUP(0) ; 分配40字节bigArray DOWOR 5000 DUP(?) ; 分配20000字节除了上⾯的例⼦以外,我们也可以直接定义常量,常量是不可以动态修改的数据类型,⼀般情况下⼀旦定义,那么在程序运⾏期间不可以被修改,常量的定义很简单,只需要将.data换成.const即可..constvar1 BYTE "hello world",0h ; 初始化为BYTE的字符串var2 DWORD 10 ; 初始化为10的DWORD类型var3 DWORD 100 dup(1,2) ; 200个DWORD的缓冲区var4 BYTE 1024 dup(?) ; 1024字节的缓冲区var5 BYTE "welcome",0dh,0ah,0 ; 0dh,0ah为换⾏符有时我们需要计算数组的⼤⼩,但⼿动计算显得特别⿇烦,此时我们可以使⽤MASM提供的$符号来进⾏数组⼤⼩的计算过程,如下..datalist BYTE 10,20,30,40,50listsize = ($ - list) ; 计算字节数据⼤⼩.datalist WORD 1000h,2000h,3000h,4000hlistsize = ($ - list) /2 ; 计算字数据⼤⼩.datalist DWORD 100000h,200000h,300000h,400000hlistsize = ($ - list) /4 ; 计算双字数据⼤⼩Post_1 equ 1000Post_2 equ 2000Post_3 equ 3000StdIn/StdOut: 使⽤masm32.inc提供的函数实现标准的输⼊与输出..386.model flat, stdcallinclude masm32.incinclude kernel32.incincludelib masm32.libincludelib kernel32.lib.datalen equ 20OutText dw ?ShowText db "请输⼊⼀个数: ",0.codemain PROCinvoke StdOut, addr ShowText ; 输出提⽰信息invoke StdIn, addr OutText,len ; 等待⽤户的输⼊invoke StdOut, addr OutText ; 输出刚才输⼊的内容retmain ENDPEND mainWriteFile: 通过调⽤系统的API函数,来实现具体的输出,其过程⽐较复杂不推荐使⽤..386.model flat, stdcallinclude windows.incinclude kernel32.incincludelib kernel32.lib.dataszText db "hello lyshark!",0.data?hOut dd ? ; 保存句柄hLen dd ? ; 保存字符长度.codemain PROCinvoke GetStdHandle,STD_OUTPUT_HANDLE ; 获取设备控制台句柄mov hOut,eax ; 把获取到的句柄给hOutinvoke lstrlen,addr szText ; 取出字符串的长度mov hLen,eaxinvoke WriteFile,hOut,addr szText,hLen,0,0 ;具体的输出retmain ENDPEND maincrt_printf: 使⽤微软C标准库中的printf函数; msvscrt.inc 把它声明做 crt_printf.386.model flat, stdcallinclude msvcrt.incincludelib msvcrt.lib.dataPrintText db "EAX=%d;EBX=%d;EDX=%d | InPut ->: ",0ScanFomat db "%s",0PrintTemp db ?.codemain PROCmov eax,10mov ebx,20mov ecx,30invoke crt_printf,addr PrintText,eax,ebx,ecx ; 打印提⽰内容invoke crt_scanf, addr ScanFomat, addr PrintTemp ; 输⼊内容并接收参数invoke crt_printf, addr PrintTemp ; 输出输⼊的内容retmain ENDPEND mainMOV指令: 从源操作数向⽬标操作数之间复制数据.00A41000 | B8 24100000 | mov eax,1024 |00A41005 | 8BD8 | mov ebx,eax |00A41007 | 66:B9 0010 | mov cx,1000 |MOVZX指令: 零扩展传送,该指令将源操作数的内容复制到⽬标操作数中,并将该值零扩展(zero-extend)⾄16位或者32位,该指令适⽤于⽆符号整数,其基本格式如下:01301000 | 66:BB 9BA6 | mov bx,A69B | BX = 0A69B01301004 | 0FB7C3 | movzx eax,bx | EAX = 0000A69B01301007 | 0FB6D3 | movzx edx,bl | EDX = 0000009B0130100A | 66:0FB6CB | movzx cx,bl | CX = 009BMOVSX指令: 符号扩展传送,该指令将源操作数的内容复制到⽬标操作数中,并将该值符号扩展(sign-extend)⾄16位或者是32位,该指令只能⽤于有符号整数,其基本格式如下:00FD1000 | 66:BB 9BA6 | mov bx,A69B | BX = 0A69B00FD1004 | 0FBFC3 | movsx eax,bx | EAX = FFFFA69B00FD1007 | 0FBED3 | movsx edx,bl | EDX = FFFFFF0B00FD100A | 66:0FBECB | movsx cx,bl | CX = FF9BXCHG指令: 数据交换指令,该指令⽤于交换两个操作数中的内容,但该指令不接受⽴即数操作数.00D71000 | B8 00100000 | mov eax,1000 | EAX = 1000h00D71005 | BB 00200000 | mov ebx,2000 | EBX = 2000h00D7100A | 93 | xchg ebx,eax | EAX = 2000h;EBX = 1000hINC/DEC指令: 数据递增与递减,INC指令⽤于对寄存器或内存数据的递增,DEC指令⽤于对寄存器或内存数据递减. 00881000 | B8 00100000 | mov eax,1000 | EAX = 1000h00881005 | 40 | inc eax | EAX = 1001h00881006 | 40 | inc eax | EAX = 1002h00881007 | BB 00200000 | mov ebx,2000 | EBX = 2000h0088100C | 4B | dec ebx | EBX = 1FFFF0088100D | 4B | dec ebx | EBX = 1FFFE0088100E | 4B | dec ebx | EBX = 1FFFDADD指令: 操作数增加,该指令⽤于将源操作数和⽬的操作数相加,且不影响源操作数的值,⽽是改变⽬的操作数.00BC1000 | B8 00100000 | mov eax,1000 | EAX = 100000BC1005 | BB 00200000 | mov ebx,2000 | EBX = 200000BC100A | 03D8 | add ebx,eax | EBX = EBX+EAX = 3000SUB指令: 操作数减少,该指令⽤于将源操作数和⽬的操作数相减,且不影响源操作数的值,⽽是改变⽬的操作数. 00811000 | B8 00200000 | mov eax,2000 | EAX = 200000811005 | BB 00100000 | mov ebx,1000 | EBX = 10000081100A | 2BC3 | sub eax,ebx | EAX = EAX-EBX = 1000AND/OR/XOR指令: 逻辑与/逻辑或/逻辑异或.00DD100E | B8 01000000 | mov eax,1 |00DD1013 | BB 01000000 | mov ebx,1 |00DD1018 | B9 00000000 | mov ecx,0 |00DD101D | 21D8 | and eax,ebx |00DD101F | 09CB | or ebx,ecx |00DD1021 | 31C0 | xor eax,eax |OFFSET操作符: 返回数据标号的偏移地址,偏移地址代表标号距数据基址的距离,单位是字节..datavar1 BYTE ?var2 WORD ?var3 DWORD ?var4 DWORD ?.codemain PROCmov esi,offset var1mov esi,offset var2mov esi,offset var3mov esi,offset var4main ENDPEND mainPTR操作符: ⽤来重载声明操作数的默认尺⼨,这在试图以不同与变量声明时所使⽤的尺⼨来访问变量时很有⽤. .datatemp DWORD 12345678h.codemain PROCmov eax,DWORD PTR [temp] ; 将temp以双字取值并存储到eaxmov ax,WORD PTR [temp] ; 将temp以字为单位取值并存储到axmov bx,WORD PTR [temp+2] ; 在偏移基础上+2main ENDPEND main00C11000 | A1 0030C100 | mov eax,dword ptr ds:[C13000] | EAX = 1234567800C11005 | 66:A1 0030C100 | mov ax,word ptr ds:[C13000] | AX = 567800C1100B | 66:8B1D 0230C100 | mov bx,word ptr ds:[C13002] | BX = 1234LENGTHOF操作符: 计算数组元素的数⽬,元素由出现在的同⼀⾏的值定义..dataArrayDW DWORD 1000,2000,3000,4000,5000,6000,7000,8000,9000,0hArrayBT BYTE 1,2,3,4,5,6,7,8,9,0h.codemain PROCmov eax,lengthof ArrayDWmov eax,lengthof ArrayBTpush 0call ExitProcessmain ENDPEND mainTYPE操作符: 返回按照字节计算的单个元素的⼤⼩..datavar1 BYTE ?var2 WORD ?var3 DWORD ?var4 QWORD ?.codemain PROCmov eax,TYPE var1 ; 1mov ebx,TYPE var2 ; 2mov ecx,TYPE var3 ; 4mov edx,TYPE var4 ; 8push 0call ExitProcessmain ENDPEND mainSIZEOF操作符: 返回等于LENGTHOF(总元素数)和TYPE(每个元素占⽤字节)返回值的乘基..datavar1 WORD 32 DUP(0) ; 32*2var2 BYTE 10,20,30,40 ; 3var3 WORD 30 DUP(?),0,0 ; 30+2var4 DWORD 1,2,3,4 ; 4.codemain PROCmov eax,SIZEOF var1mov eax,SIZEOF var2mov eax,SIZEOF var3mov eax,SIZEOF var4main ENDPEND mainLOOP循环(普通循环): 该指令检测ECX寄存器的变化,每次循环寄存器⾃动减1,当ECX=0循环结束,否则继续循环..codemain PROCmov ecx,10 ; 计数循环寄存器初始化为10top: ; 循环标号,编译器会将其转换成⼀个地址xor eax,eaxmov eax,ecxloop top ; loop跳转到指定地址,此处为toppush 0call ExitProcessmain ENDPEND mainLOOP循环(循环中使⽤ECX): 如果⽤光了所有的通⽤寄存器,但⼜必须要使⽤ECX的话,可以在循环开始将ECX保存..datacount DWORD ?.codemain PROCmov ecx,10top:mov count,ecx ; 将ecx寄存器放⼊count变量xor ecx,ecxmov ecx,1000 ; 重置ecx寄存器的数值add eax,ecxmov ecx,count ; 处理完成后,恢复ECX寄存器loop top ; 继续循环push 0call ExitProcessmain ENDPEND mainLOOP循环(嵌套循环): 在循环内部创建另⼀个循环的时候,必须考虑外层ECX中的外层循环计数该如何处理,把外层循环计数保存在内存中,是⾮常的理想的..datacount DWORD ?.codemain PROCmov ecx,10 ; 设置外层循环计数L1:mov count,ecx ; 保存外层循环计数mov ecx,20 ; 设置内层循环计数L2:xor eax,eaxxor ebx,ebxxor edx,edxloop L2 ; 重复内层循环计数mov ecx,count ; 恢复外层循环计数器loop L1 ; 执⾏外层循环跳转push 0call ExitProcessmain ENDPEND mainIF-ENDIF(伪指令):.codemain PROCmov eax,100mov ebx,200.IF (eax == ebx) && (ebx == ebx)xor eax,eaxxor ebx,ebx.ELSEIF (eax >= 100) || (ebx == ebx)add eax,100add ebx,100.ENDIFmain ENDPEND mainWHILE-ENDW(伪指令):.dataCount DWORD 10SumNum DWORD 0.codemain PROCxor eax,eax.WHILE (eax < Count)add SumNum,1inc eax.ENDWmain ENDPEND mainREPEAT-UNTIL(伪指令): 以下代码利⽤循环伪指令,完成了1-10相加..dataCount DWORD 10SumNum DWORD 0.codemain PROCxor eax,eax.REPEATinc eaxadd SumNum,1.UNTIL (eax >= Count)main ENDPEND mainBREAK(伪指令): 以下是个死循环,当eax寄存器的值等于5时,则执⾏.break结束程序的运⾏. .codemain PROCmov eax,10.while (1)dec eax.break .if(eax == 5).endwretmain ENDPEND mainCONTINUE(伪指令): 当EAX的值⼩于等于5时执⾏continue,否则执⾏inc ebx,总循环数为10. .codemain PROCmov eax,0mov ebx,0.repeatinc eax.continue .if(eax <= 5)inc ebx.until (eax >= 10)retmain ENDPEND mainFOR 字符替换(伪指令): 该伪指令并不是循环,⽽是分别将指定的指令批量的替换到程序中. .codemain PROCfor num,<1,2,3>xor eax,eaxadd eax,DWORD PTR [num]endmretmain ENDPEND mainFORC字串替换(伪指令): 该伪指令并不是循环,⽽是分别将指定的字串批量的替换到程序中..codemain PROCforc code,<@#$%^&*()<>>BYTE "&code"endmretmain ENDPEND mainWindows系统默认运⾏于保护模式下,当处理器运⾏于保护模式下时,每个程序可以寻址4GB的内存范围,地址范围是从⼗六进制数的0-FFFFFFFF,微软汇编器的平坦模式,适⽤于保护模式编程,在平坦模式下其内存寻址的⽅式包括,直接寻址,间接寻址,基址变址寻址,⽐例因⼦寻址等,接下来将分别来演⽰.◆直接寻址◆在声明变量名称的后⾯加上⼀个偏移地址,可以创建直接偏移(direct-offset)操作数,可以通过它来访问没有显⽰标号的内存地址,接下来看⼀个实验例⼦:.dataArrayB BYTE 10h,20h,30h,40h,50hArrayW WORD 100h,200h,300h,400hArrayDW DWORD 1h,2h,3h,4h.codemain PROC; 针对字节的寻址操作mov al,[ArrayB] ; al=10mov al,[ArrayB+1] ; al=20mov al,[ArrayB+2] ; al=30; 针对内存单元字存储操作mov bx,[ArrayW] ; bx=100mov bx,[ArrayW+2] ; bx=200mov bx,[ArrayW+4] ; bx=300; 针对内存单元双字存储操作mov eax,[ArrayDW] ; eax=00000001mov eax,[ArrayDW+4] ; eax=00000002mov eax,[ArrayDW+8] ; eax=00000003main ENDPEND main◆间接寻址◆在处理数组操作时完全使⽤直接寻址是不切实际的,我们不⼤可能为数组的每个元素都提供⼀个不同的标号,也不太可能使⽤⾮常多的常量偏移地址去寻址数组的各个元素,处理数组唯⼀可⾏的⽅法是⽤寄存器作为指针并操作寄存器的值,这种⽅法称为间接寻址(indirect addressing),操作数使⽤间接寻址时,就称为间接操作数(indirect operand).通过ESI内存寻址: 通过使⽤ESI寄存器,外加偏移地址(此处DWORD=4字节),实现寻址..dataArrayDW DWORD 10000h,20000h,300000h.codemain PROCmov esi,offset ArrayDW ; 获取数据段的内存基址mov eax,[esi] ; 取出[esi]地址中的数据,并赋值给eaxadd esi,4 ; 每次esi指针加4,因为数据格式为DWORDmov eax,[esi]add esi,4mov eax,[esi]main ENDPEND main通过ESP堆栈寻址: 通过ESP堆栈寄存器,实现寻址..codemain PROCmov eax,100 ; eax=1mov ebx,200 ; ebx=2mov ecx,300 ; ecx=3push eax ; push 1push ebx ; push 2push ecx ; push 3mov edx,[esp + 8] ; EDX = [ESP+8]=1mov edx,[esp + 4] ; EDX = [ESP+4]=2mov edx,[esp] ; EDX = [ESP]=3main ENDPEND main◆变址寻址◆变址寻址,变址操作数(indexed operand)把常量和寄存器相加以得到⼀个有效地址,任何32位通⽤寄存器都可以作为变址寄存器,MASM允许使⽤两种不同的变址操作数据格式.变量名+寄存器: 通过变量名和寄存器结合,变量名代表变量偏移地址的常量,通过变更ESI寄存器的值进⾏数据寻址..dataArrayDW DWORD 10000h,20000h,300000h.codemain PROCmov esi,0mov eax,[ArrayDW + esi] ; 通过变量名+esi寄存器寻址mov ebx,8 ; 增加8字节mov eax,[ArrayDW + ebx] ; 定位第三个DW数据内存main ENDPEND main基址+偏移: 通过把变址寄存器和内存偏移常量结合,⽤寄存器存放数组基址,⽤常量标识各个数组元素..dataArrayW WORD 1000h,2000h,3000h,4000h.codemain PROCmov esi,offset ArrayW ; 获取基址mov ax,[esi] ; 显⽰第⼀个数据mov ax,[esi + 2] ; 显⽰第⼆个数据mov ax,[esi + 4] ; 最后⼀个main ENDPEND main基址变址寻址: 通过计算公式,这⾥数组中每个元素占⽤4字节,所以需要乘以4,寄存器ECX为需要定位的元素偏移..dataArray DWORD 1000h,2000h,3000h,4000h,0h.codemain PROClea eax,Arraymov ecx,2mov edx,DWORD PTR [eax + ecx * 4] ;edx=3000hmov ecx,1mov edx,DWORD PTR [eax + ecx * 4] ;edx=2000hmain ENDPEND main⽐例因⼦寻址: 通过使⽤⽐例因⼦,以下例⼦每个DWORD=4字节,且总元素下标=0-3,得出⽐例因⼦3* type arrayDW..dataArrayDW DWORD 1000h,2000h,3000h,4000h.codemain PROC; 第1种⽐例因⼦寻址mov esi,3*type ArrayDW ;总共3个下标x每个元素的类型mov eax,ArrayDW[esi]; 第2种⽐例因⼦寻址mov esi,3 ; 变更ESI下标,可实现定位不同的数据mov eax,ArrayDW[esi*4] ; 其中4代表每个数据类型4字节; 第3种⽐例因⼦寻址mov esi,3mov eax,ArrayDW[esi*type ArrayDW]main ENDPEND main指针寻址: 变量地址的变量称为指针变量(pointer variable),Intel处理器使⽤两种基本类型的指针,即near(近指针)和far(远指针),保护模式下使⽤Near指针,所以它被存储在双字变量中..dataArrayB BYTE 10,20,30,40,50ArrayD DWORD 1,2,3,4,5ptrB DWORD OFFSET ArrayB ; 指针ptrB --> ArrayBptrD DWORD OFFSET ArrayD ; 指针ptrD --> ArrayD.codemain PROCmov esi,ptrB ; 指向数组ArrayBmov al,[esi] ; 取出 10hmov esi,ptrD ; 指向数组ArrayDmov eax,[esi] ; 取出 1hmain ENDPEND main在学习数据⽐较指令之前,需要先来了解⼀下标识寄存器这个东西,标志寄存器⼜称程序状态寄存器(Program Status Word,PSW),这是⼀个存放条件码标志,控制标志和系统标志的寄存器.标志寄存器中存放的有条件标志,也有控制标志,它对于处理器的运⾏和整个过程的控制有着⾮常重要的作⽤.条件标志主要包括进位标志、奇偶标志、辅助进位标志、零标志、符号标志、溢出标志等,控制标志主要有跟踪标志,因为有标志寄存器的存在才能实现各种华丽的判断循环等,常⽤的标志有以下6个:标志位标志全称标志序号标志位说明CF(Carry Flag)进位标志位0当执⾏⼀个加法(或减法)运算,使最⾼位产⽣进位(或借位)时,CF为1;否则为0PF(Parity Flag)奇偶标志位2当运算结果中,所有bit位(例:1001010)中1的个数为偶数时,则PF=1;为基数PF=0AF(Auxiliary Flag)辅助进位标志4执⾏加法(减法)运算,结果的低4位向⾼4位有进位(借位)时,则AF=1;否则AF=0ZF(Zero Flag)零标志位6若当前的运算结果为零,则ZF=1;否则ZF=0SF(Sign Flag)符号标志位7若运算结果为负数,则SF=1;若为⾮负数则SF=0TF(Trap Flag)陷阱标志位8为⽅便程序调试⽽设计的,TF=1单步执⾏指令,TF=0则CPU正常执⾏程序IF(Interrupt)中断允许标志9当IF=1CPU可响应可屏蔽中断请求,当设置IF=0则CPU不响应可屏蔽中断请求DF(Direction)⽅向标志位10当DF=0时为正向传送数据(cld),否则为逆向传送数据(std)OF(Overflow)溢出标志位11记录是否产⽣了溢出,当补码运算有溢出时OF=1;否则OF=0ZF零标志位: ZF标志相关指令执⾏后,结果为0则ZF=1;若结果不为0则ZF=0.00C31000 | 90 | nop | ZF = 000C31001 | B8 01000000 | mov eax,1 | ZF = 000C31006 | 83E8 01 | sub eax,1 | ZF = 100C31000 | 90 | nop | ZF = 000C31001 | B8 02000000 | mov eax,2 | ZF = 000C31006 | 83E8 01 | sub eax,1 | ZF = 0PF奇偶标志位: PF标志相关指令执⾏后,其结果所有bit位中的1若为偶数,则PF=1;若为奇数PF=0.00C31000 | 90 | nop | PF = 000C31001 | B8 00000000 | mov eax,00000000 | PF = 000C31006 | 83C0 6F | add eax,00000111 | PF = 100C31000 | 90 | nop | PF = 000C31001 | B8 00000000 | mov eax,00000000 | PF = 000C31006 | 83C0 6F | add eax,00000011 | PF = 0SF符号标志位: SF标志相关指令执⾏后,其结果是否为负,若为负则SF=1;若为⾮负SF=0.00C3100B | 90 | nop | SF = 000C3100C | B8 E8030000 | mov eax,3E8 | SF = 000C31011 | 2D E9030000 | sub eax,3E9 | SF = 100C3100B | 90 | nop | SF = 000C3100C | B8 E8030000 | mov eax,3E8 | SF = 000C31011 | 2D E9030000 | sub eax,3E8 | SF = 0CF进位标志位: CF标志相关指令执⾏后,在进⾏⽆符号运算时,如果表达式发⽣进位或借位则CF=1.00C31016 | 90 | nop | CF = 000C31017 | 66:B8 FFFF | mov ax,FFFF | CF = 000C3101B | 66:83C0 01 | add ax,1 | CF = 100C31016 | 90 | nop | CF = 000C31017 | 66:B8 FFFF | mov ax,FFFF | CF = 000C3101B | 66:83C0 01 | sub ax,1 | CF = 0OF溢出标志位: OF标志相关指令执⾏后,超出机器所能表⽰的范围称为溢出若发⽣了溢出OF=1;否则OF=0.00C3101B | 90 | nop | OF = 000C3101C | B0 40 | mov al,64 | OF = 000C3101E | 04 40 | add al,64 | OF = 100C31020 | 90 | nop | OF = 000C31021 | B0 3F | mov al,63 | OF = 000C31023 | 04 40 | add al,64 | OF = 0TEST指令: 该操作与AND指令类似,唯⼀不同的是它不保存结果,常⽤来测试标志位状态.00DD103B | B8 01000000 | mov eax,1 | EAX = 100DD1040 | BB 00000000 | mov ebx,0 | EBX = 000DD1045 | 85D8 | test eax,ebx | ZF = 100DD1051 | B8 01000000 | mov eax,1 |00DD1056 | A9 00000000 | test eax,0 | ZF = 100DD105B | 83E0 00 | and eax,0 | ZF = 100DD1062 | 83C8 01 | or eax,1 | ZF = 0CMP指令: 在源操作数和⽬标操作数进⾏减法操作,只影响标志位.00DD1001 | B8 00010000 | mov eax,100 | EAX = 10000DD1006 | BB 50000000 | mov ebx,50 | EBX = 5000DD100B | 39D8 | cmp eax,ebx | eax - ebx00DD100D | 0F87 EDFF62FF | ja 401000 | jump注记符跳转条件描述信息JZ/JE ZF=1为零则跳转,(leftOp - rightOp = 0)JNZ/JNE ZF=0不为零则跳转,(leftOp - rightOp != 0)JC/JNC CF=1/0设置进位标志则跳/未设置进位标志则跳JO/JNO OF=1/0设置溢出标志则跳/未设置溢出标志则跳JS/JNS SF=1/0设置符号标志则跳/未设置符号标志则跳JP/JNP PF=1/0设置奇偶标志则跳(偶)/未设置奇偶标志则跳(基)⽆符号模式有符号模式跳转条件描述信息JA JG(left > right)⼤于则跳转JAE JGE(left >= right)⼤于或等于则跳转JB JL(left < right)⼩于则跳转JBE JLE(left <= right)⼩于或等于则跳转JZ/JE通⽤跳转: 检测到ZF=1也就说明表达式返回了0,则程序跳转,否则不跳转. 01031001 | B8 00010000 | mov eax,64 | eax=10001031006 | BB 00010000 | mov ebx,64 | ebx=1000103100B | 39D8 | cmp eax,ebx | eax-ebx0103100D | 0F84 EDFF3CFF | je 401000 | jump01031013 | 0F84 E7FF3CFF | jz 401000 | jumpJNZ/JNE通⽤跳转: 检测到ZF=0也就说明表达式返回了1,则程序跳转,否则不跳转. 01031001 | B8 00010000 | mov eax,65 | eax=10101031006 | BB 00010000 | mov ebx,64 | ebx=1000103100B | 39D8 | cmp eax,ebx | eax-ebx0103100D | 0F84 EDFF3CFF | jne 401000 | not jump01031013 | 0F84 E7FF3CFF | jnz 401000 | not jumpJA/JB⽆符号跳转: 基于⽆符号数的跳转指令,JA⼤于则跳转或JB⼩于则跳转.01031001 | B8 64000000 | mov eax,64 | eax=10001031006 | BB C8000000 | mov ebx,C8 | ebx=2000103100B | 3BD8 | cmp ebx,eax | ebx-eax0103100D | 0F87 EDFF3CFF | ja 401000 | ebx>eax jump0103100F | B8 64000000 | mov eax,64 | eax=10001031014 | BB 32000000 | mov ebx,32 | ebx=5001031019 | 3BD8 | cmp ebx,eax | ebx-eax0103101B | 0F82 DFFF3CFF | jb 401000 | ebx<eax jump01031001 | B8 64000000 | mov eax,64 | eax=10001031006 | BB 64000000 | mov ebx,64 | ebx=1000103100B | 3BC3 | cmp eax,ebx | eax-ebx0103100D | 0F87 EDFF3CFF | ja 401000 | eax=ebx not jump01031013 | 0F82 E7FF3CFF | jb 401000 | eax=ebx not jumpJAE/JBE⽆符号跳转: 基于⽆符号数的跳转指令,JAE⼤于等于则跳转或JBE⼩于等于则跳转. 01031001 | B8 64000000 | mov eax,64 | eax=10001031006 | BB 64000000 | mov ebx,64 | ebx=10001031010 | 3BC3 | cmp eax,ebx | eax-ebx01031012 | 0F83 E8FF3CFF | jae 401000 | eax>=ebx jump01031001 | B8 64000000 | mov eax,64 | eax=10001031006 | BB C8000000 | mov ebx,C8 | ebx=2000103100B | 3BD8 | cmp ebx,eax | ebx-eax0103100D | 0F83 EDFF3CFF | jae 401000 | ebx>=eax jump01031001 | B8 C8000000 | mov eax,C8 | eax=20001031006 | BB 64000000 | mov ebx,64 | ebx=1000103100B | 3BD8 | cmp ebx,eax | ebx-eax0103100D | 0F86 EDFF3CFF | jbe 401000 | ebx<=eax jump。
Win32汇编语言程序设计

(段的简化定义) (用了 .STARTUP)
特别说明:
对于 Windows下的32位段程序,可以用如下说明: .386 .model flat, stdcall
系统会自动的将 .DATA段,.DATA?段,.CONST段,.STACK段 组合成一个组 DGROUP。 自动的将 DS,ES,SS设置成组DGROUP的段址。
(1) 段的简化定义 用简单的方式定义代码段、数据段、堆栈段等。 ① 存储模型说明伪指令MODEL 格式: .MODEL 存储模型 [,语言类型] [,系统类型][,堆栈选项] 功能:用于指定程序中各个段的属性、程序 的运行环境,调用规则。
存储模型 TINY SMALL COMPACT MEDIUM LARGE HUGE FLAT
WINDOWS 环境下32 位 汇编语言程序设计
1 编程环境
编辑工具:记事本、UltraEdit32、EditPlus等 汇编程序: ML.EXE (Masm and Link) 6.11 版本及以上 连接程序:Link.EXE 调试工具:TD32.exe 资源编译器:RC.EXE, CVTRES.EXE 常用的API函数申明及引入库 从汇编网站上下载: WIN32编程环境(不需安装)
堆栈段定义:.STACK [堆栈字节数] 字节数的缺省值是1024
常数(只读)数据段定义:.CONST 程序开始伪指令:.STARTUP
源程序中每一个段定义都不再需要单独的段结束伪 指令,但源程序的最后仍需要用END伪指令结束。
特别说明:
对于DOS下的16位段程序,也可以使用简化 段定义。只是要先写 .MODEL SMALL 再写 .386 仍然使用DOS下的汇编和连接程序。 程序示例: c6_242_1.asm c6_242_2.asm
Win32汇编语言程序设计.

; here begins our .data section .data Text Caption db "Hello, World!",0 db "Hello",0 ;Text, terminated with "0" ;Caption string, 0-terminated
Win32 API的调用
函数调用后的返回值保存在EAX寄存器中
• 涉及字符串的API函数
MessageBoxA MessageBoxW ANSI(8位) Windows 9x
Unicode(16位) Windows NT
Win32 API的调用
•示例
; set options for the assembler .386 .model flat, stdcall ; declaration of all used API-functions ExitProcess MessageBoxA PROTO PROTO :DWORD :DWORD, :DWORD, :DWORD, :DWORD
Win32 API的调用
.386 .model flat, stdcall include windows.inc include user32.inc include kernel32.inc include gdi32.inc .data Text Caption .code Start: INVOKE INVOKE end Start db "Hello, World!",0 db "Hello",0 ;Text, terminated with "0" ;Caption string, 0-terminated
32位汇编语言——表达式与操作符.

2018/9/14
10
逻辑运算符
实现按位相与、相或、异或、求反的逻辑 运算 or al,03h AND 45h ;等价于 or al,01h
47H AND 0FH,NOT 56H 计算结果分别为:7和0A9H
2
实现对数值的左移、右移的逻辑操作;移 入低位或高位的是0 格式为: 数值表达式 SHL/SHR 移位次数 mov al, 0101b SHL (2*2) ;等价于 mov al,01010000b
word_Buffer dw szBuffer byte
; 在byte类型变量的定义中,用引号定义字符串和数值定义的方法混用
szText db 'Hello,world!',0dh,0ah,'Hello again',0dh,0ah,0
2018/9/14
5
局部变量
两个以上子程序都要用到的数 据才被定义为全局变量统一放 在数据段中,仅在子程序内部 使用的变量则放在堆栈中 在进入子程序的时候,通过修 改堆栈指针esp来预留出需要的 空间,在用ret指令返回主程序 之前,同样通过恢复esp丢弃这 些空间 空间是临时分配的,所以无法 定义含有初始化值的变量,对 局部变量的初始化一般在子程 序中由指令完成。
EQ NE GT LT GE LE
NOT
低
AND
OR XOR
2018/9/14
16
地址表达式
地址表达式是计算存 储单元地址的表达式, 它可由标号、变量名 和由括号括起来的基 址或变址寄存器组成。 其计算结果表示一个 存储单元的地址,而 不是该存储单元的值。
B1
DB DB
汇编语言equ的用法

汇编语言equ的用法汇编语言是一种底层的编程语言,直接操作计算机硬件,可以实现高效的算法和程序。
在汇编语言中,equ是一个非常重要的关键字,用于定义符号常量。
equ是equation的缩写,意思是“等式”。
在汇编语言中,我们可以使用equ关键字来定义一个符号常量,它的值在整个程序中不会发生改变。
这样做的好处是可以提高程序的可读性和可维护性,因为我们可以使用有意义的符号代替数字或字符串,更加清晰地表达程序的含义。
equ的语法格式为:符号名 equ 表达式其中符号名是我们所定义的常量的名字,可以使用任何合法的汇编语言标识符;表达式是一个算术表达式,可以包含数字、符号常量、运算符等。
下面是一个例子,定义了一个名为counter的符号常量,它的值为10:counter equ 10在程序中,我们可以使用counter代替数字10,如下所示:mov eax, counter ;将eax寄存器的值设置为10使用符号常量可以使程序更加易读易懂,并且方便修改。
如果我们要修改counter的值,只需要修改它的定义,整个程序中所有使用counter的地方都会自动更新。
除了定义数字常量,我们还可以使用equ定义字符串常量,如下所示:msg db 'Hello, world!', 0这里我们定义了一个字符串常量msg,它的值为“Hello, world!”,0是字符串的结束符号。
我们可以使用msg来输出这个字符串:mov eax, 4 ;调用系统调用4,输出字符串mov ebx, 1 ;输出到标准输出mov ecx, msg ;msg是字符串常量的符号名mov edx, 13 ;字符串的长度int 0x80 ;调用系统调用在程序中使用符号常量可以使程序更加清晰易懂,并且方便修改。
但是需要注意的是,符号常量只是一种宏定义,它不会分配内存空间,也不会检查定义的有效性。
因此,在使用符号常量时,需要确保其定义的正确性和合理性。
经典课件:符号常量及其定义方法

.
29
整型和实型的算术量四则运算的规律见表3。
表 .3
30
乘方运算的规律见表4。
表4
.
31
类型的转换是从左向右进行的,在遇到不同类 型的算术量时才进行转换。例如:
1/4*20.0。并不是一开始就同时将1和4转换成实 数1.0和4.0然后进行实数运算(得5.0),而是 先进行整数运算1/4得0,然后再乘以20.0,使结果转 化为实型,最后结果为0.00000。
.
23
[6] FORTRAN算术表达式的求值运算的优先次序为:
① 括号 ② 函数 ③ ** ④ *,/ ⑤ +,-
高
低
例如 SQRT(3.5 * 2)** 2的求值过程为:
[1] 先算括号内的3.5*2;
[2] 再进行平方根的计算;
[3] 最后进行乘方运算。
.
24
表. 2
25
(4) 算术表达式运算中的类型问题 FORTRAN 77允许不同类型的算术量(包括整型、
相应的FORTRAN表达式应该为: (((a+b)**2+(a-b)**2)** 3+c)+6
当括号层次比较多时往往容易弄错或漏写一侧 括号,要十分小心。 [4]如果连续使用乘方符号,如4**3**2,FORTRAN
77规定,对多次乘方按“先右后左”原则处理, 即先计算3**2得9,再进行49的运算。为避免出错,
.
10
(3) 函数的自变量是有类型的,函数值也是有类型的,例如 MOD(8,3)中自变量8和 3是整型,函数MOD(8,3)的值“2” 也是整型,如果写成MOD(8.0,3.0),自变量是实型的,
函数值也是实型的,其值为2.0。要特别注意,当自变量 的个数为两个或两个以上时,它们的类型必须一致,