📄 汇007.txt
字号:
.END
7.5.10 局部变量的定义
局部变量的定义格式:
LOCAL 变量名[[数量]] [:数据类型] [,变量名[[数量]] [:数据类型]]...
伪指令LOCAL的作用是说明一个或多个临时的局部变量(位于堆栈中)。局部变量必须在任何指令之前加以说明,并可用多个LOCAL伪指令来说明其局部变量。
在子程序中,若说明了某个局部变量,则子程序体中的指令就可使用该局部变量。汇编程序会把对它的引用转换成用指针寄存器BP来访问其在堆栈中的实际存储单元。
在局部变量的作用域与高级语言中局部变量的作用域相一致,即:局部变量只能在当前子程序中使用,离开该子程序,它们就不能再被引用。但在局部变量的命名规则上有所不同,高级语言中的局部变量可与外层变量同名,而汇编语言中的局部变量不能与其它任何变量同名,否则,在汇编时,将会给出“重定义”(Symbol redefinition)的错误信息。
“数量”用来说明该变量所具有的元素个数。象高级语言的数组定义一样,该数量必须写在括号“[ ]”之中。“数量”说明项是可选项。
局部变量的类型说明符可以是任何合法的数据类型说明符。在16位段环境下,该缺省的数据类型是WORD,而在32位段环境下,该缺省的数据类型是DWORD。
此处伪指令LOCAL的作用与9.3.1节中伪指令LOCAL的作用是完全不同的,具体的差异请见9.3.1节中的比较。
例如:
LOCAL data[20]:BYTE, num:WORD
在上例的说明中,定义了二个局部变量:data和num。前者是字节类型,并有20个元素,后者是字类型,只有其自身1个元素。
7.6 子程序库
库文件对学过C/C++语言程序设计的读者来说应该是不会陌生的,该语言的程序设计环境提供了大量的库文件,也就是说,提供了大量的标准函数或过程。在本节里,介绍读者如何创建自己的库文件。
7.6.1 建立库文件命令LIB
宏汇编MASM系统提供了建立库文件的命令文件LIB.EXE。其通常是在命令行环境(MS-DOS方式)下使用的,当然,也可在Windows 95/98等环境下利用其“开始”菜单下的“运行”功能项来使用。
一、MS-DOS系统
显示命令LIB用法的命令如下:
…>lib /?
该命令的显示结果如图7.9中所示。
二、Windows系统
图7.8 运行LIB命令的画面
图7.9 显示LIB命令功能的画面
命令LIB的使用方式和显示结果如图7.8和7.9所示。
三、命令显示内容的解释
1)、各选项的解释
选项 含义
/?、/HELP 显示LIB命令的用法,描述各命令行参数的含义
/IGNORECASE 忽略子程序名中的大小写 在实践中,作用不明显
/NOIGNORECASE 不忽略子程序名中的大小写
/NOEXTDICTIONARY 不建立扩展的目录
/NOLOGO 不显示版本号和版权信息
/PAGESIZE:n 设置库文件的每页字节数为n
2)、命令项的解释:
选项 含义
+name 向库文件中加一个新的目标文件
-name 从库文件中删除一个指定的目标文件
-+name 用新的目标文件替换掉库文件中原有的目标文件
*name 拷贝出指定的目标文件
-*name 从库文件中移出指定的目标文件
在弄懂了LIB的各项功能含义后,读者就可根据自己的需要来建立库文件了。
7.6.2 建立库文件举例
假设现有目标文件sub1.obj、sub2.obj和sub3.obj,要用它们建立库文件mylib.lib。可用下列方法来建立该库文件:
方法1:所有目标文件都准备好了,可一次性把它们加入到库文件中
…>lib mylib +sub1 +sub2 +sub3
方法2:随着目标文件的逐个生成,而依次把它们加入到库文件中
…>lib mylib +sub1
…>lib mylib +sub2
…>lib mylib +sub3
假如源文件sub3.asm已修改,并也生成了新的目标文件sub3.obj,这时,就需要把库文件mylib.lib中的sub3.obj替换成新的目标文件。于是,可用下面命令来实现替换:
…>lib mylib -+sub3
当提示输入目标库文件名(Output library)时,可按“回车”用默认的原库文件名。
如果想查看库文件mylib.lib中各文件的大小和存放的先后次序,可用下列命令:
…>lib mylib, list ;把库文件mylib.lib中的文件结构生成到文件list中
…>type list
7.6.3 库文件的应用
在开发一个功能较弱的应用程序时,其执行文件通常可由一个目标文件连接而成,当开发一个功能较强、关系较复杂的应用程序时,其执行文件很难由一个目标文件连接而成,常常是由多个目标文件(模块)连接而成的。各模块之间无疑会存在着相互调用、相互访问数据单元等内在联系,各模块之间的相互联系就产生了这样的问题:程序员如何在源程序中来表达这种联系?
为了解决描述各模块之间的联系,汇编语言提供了二条伪指令PUBLIC和EXTRN,它们的作用有点象C/C++语言说明变量、过程和函数是“全局的”或“外部的”。
这二条伪指令的具体用法和含义如下:
1、伪指令PUBLIC
伪指令PUBLIC是用来说明:当前模块中哪些标识符是能被其它模块引用的公共标识符。其说明的一般格式如下:
PUBLIC 标识符1, 标识符2, ……
其中:“标识符”可以是变量名、过程名和程序标号,各标识符之间要用逗号分开。
上面说明语句说明了标识符1、标识符2等是公共标识符,可以被其它模块引用。在一个模块中,可用多条PUBLIC伪指令来说明公共标识符。
2、伪指令EXTRN
伪指令EXTRN是用来说明:在当前模块所使用的标识符中,哪些标识符是已在其它模块中被定义为指定类型的标识符。如果当前模块使用了其它模块的标识符,而对它又不加以说明的话,那么,在汇编时,汇编程序将会给出下列出错信息:
error nnnnn: undefined symbol : XXXXXX
其中:“nnnnn”是错误号,“XXXXXX”是当前模块中没有定义的标识符。
伪指令EXTRN的一般说明格式如下:
EXTRN 标识符1:类型1, 标识符2:类型2, ……
其中:“标识符”和“类型”之间要用冒号“:”连接。
上面语句说明了标识符1、标识符2等是外部标识符,它们在其它模块中已被分别定义为类型1、类型2等,该类型说明符可以是:NEAR、FAR、BYTE、WORD、DWORD等之一。如果在一条说明伪指令中说明了多个标识符,那么,各标识符之间要用逗号分开。
在一个模块中,可用多条EXTRN伪指令来说明本模块所引用的外部标识符。
注意:伪指令EXTRN中所说明的标识符必须在其定义的模块中被PUBLIC伪指令说明为公共标识符,并且其说明的标识符类型要与该标识符在定义是的类型相一致,否则,要么不能生成其可执行文件,要么其执行文件不能正确运行。
例7.7 把例7.3、7.4和7.5合并在一起生成一个可执行文件,假设它们所对应的源程序名分别为Count.ASM、DispBX.ASM和Main.ASM。
解:由于在源文件Count.ASM中调用了子程序UPPER,所以,例7.1的程序也必须加入到本题中。假设其源文件名为Upper.ASM。
由于生成本题的执行文件需要四个模块,模块之间存在着调用关系,所以,在有关源文件中需要说明某些标识符为外部属性,或说明其为公共属性。
为了把前面例子中的子程序改写成可汇编的程序,需要添加一些简单的说明语句或进行简单修改,其添加或改写的部分已在下面用“下划线”表示出来。 ;源文件Upper.ASM
;子程序说明信息:……
PUBLIC UPPER
SegUpr SEGMENT 'code'
UPPER PROC FAR
…… ;例7.1中的程序段,在此从略
UPPER ENDP
SegUpr ENDS
END
;源文件DispBX.ASM
;子程序说明信息:……
PUBLIC DISPBX
SubData SEGMENT
DB
5 DUP('0'), 0ah, 0dh, '$'
SubData ENDS
SegDisp SEGMENT 'code'
DISPBX PROC FAR
…… ;例7.3中的程序段,在此从略
DISPBX ENDP
SegDisp ENDS
END
;源文件Count.ASM
;子程序说明信息:……
PUBLIC COUNT
EXTRN UPPER:FAR
SegCount SEGMENT 'code'
COUNT PROC FAR
…… ;例7.4中的程序段,在此从略
COUNT ENDP
SegCount ENDS
END
;源文件Main.ASM
EXTRN COUNT:FAR, DISPBX:FAR
.MODEL SMALL
.DATA
STR DB 'KSDJ L0984/[]3oiu OIU OIU (*&(5341', 0
.CODE
.STARTUP
LEA DX, STR
CALL COUNT ;调用子程序统计出各类字符的个数
CALL DISPBX ;调用子程序显示数字字符的个数
MOV BX, CX
CALL DISPBX ;调用子程序显示字母的个数
MOV BX, DI
CALL DISPBX ;调用子程序显示其它字符的个数
.EXIT 0
END
经过以上改写后,可用下列命令把它们分别汇编成目标文件(假设已安装了MASM编程环境):
…>MASM upper
…>MASM dispbx
…>MASM count
…>MASM main
有了这些目标文件后,可用以下二种方法来生成可执行文件。
方法1:把所有的目标文件连接在一起
…>link main+upper+count+dispbx
方法2:把目标文件upper.obj、count.obj和dispbx.obj加到自己开发的库文件中,然后在连接时,与该库文件连接。
…>lib mylib +upper +count +dispbx
…>link main
Microsoft (R) Segmented Executable Linker Version 5.31.009 Jul 13 1992
Copyright (C) Microsoft Corp 1984-1992. All rights reserved.
Run File [main.exe]:
List File [nul.map]:
Libraries [.lib]: mylib ;输入要连接的库文件,可用加号“+”连接多个库文件
Definitions File [nul.def]:
LINK : warning L4021: no stack segment
…>main ;运行生成的文件
以上各步骤也可由集成开发环境PWB来完成,具体介绍请见附录1。另外,当模块的指令条数较少时,也可以把几个子模块合在一个源文件中。
7.6.4 库文件的好处
程序员在编写源程序时,通常采用模块化的思想来组织源程序:把各类不同的子程序分别编写在不同的源程序中,在各源程序中说明所用到的在其它模块中定义,或说明本模块的定义子程序可被其它模块调用。这样组织后,就可以分别汇编它们而得到其相应的目标文件,在有了这些目标文件后,就可生成最终的可执行文件,但可用不同的方法来生成最终的可执行目标文件。
方法1:直接连接目标文件而生成可执行文件(如上节例7.6中的方法1所示)
这种方法简单、方便,也是常用的一种方法,但在连接时,LINK程序会把目标文件中的所有代码都嵌入到执行文件中,从而使得:包含在某目标文件中、但并没有被调用的子程序代码也出现在执行文件中。这种情况无疑增加了执行文件的字节数。
方法2:采用子程序库的方法(如上节例7.6中的方法2所示)
库文件可以把它看成是子程序的集合。库文件中存储着子程序名、子程序的目标代码以及连接所需要的重定位信息。当某目标文件与库文件相连接时,LINK程序只把目标文件所用到的子程序从库文件中找出来,并合并到最终的可执行文件中,而不是把库中所含的全部子程序都纳入最后的可执行文件。
对照方法1和2可知:用库文件来存储子程序可生成较短的执行文件。
7.7 习题
7.1、汇编语言的子程序是如何定义的?所使用的关键字有哪些?
7.2、为了编写具有良好风格的子程序,一般需要书写哪些重要的说明性信息?
7.3、简述调用指令CALL和转移指令JMP之间的主要区别。
7.4、简述段内和段间子程序调用指令CALL的主要区别。
7.5、子程序返回指令RET的功能能否用JMP指令来模拟,若可以,请用段内子程序的返回加以说明,否则,说明理由。
7.6、子程序返回指令“RET 6”的功能是返回数值“6”给调用程序吗?若不是,那其作用是什么?
7.7、在子程序中要使其所用寄存器对调用者是透明的,请举例说明达到其目的的方法。
7.8、编写子程序实现下列功能,参数的传递方式可自行决定(假设所有变量都是字类型)。
1)、ABS(x)=|x|
2)、f(x)=3x2 + 5x – 8
3)、strlen(String) //返回字符串String的长度,该字符串以0结束
4)、判断三条边a、b和c能否构成三角形,若能,CF为1,否则,CF为0
7.9、编写计算将CX值三次方的子程序,结果也存入CX(不考虑溢出问题)。
7.10、编写计算表达式AX=DI×SI/100H的子程序(不考虑溢出问题)。
7.11、编写计算EAX=EAX+EBX+ECX+EDX的子程序。在计算过程中,若产生进位,则EDI=1,否则,EDI=0。
7.12、在MASM 6.x编程环境下,简述子程序定义中USE说明语句的作用是什么?
7.13、调用子程序指令CALL和调用伪指令INVOKE的主要区别是什么?
7.14、在子程序的完全定义形式中,其语言类型所起的作用是什么?可用哪二种方法来设定子程序的语言类型?
7.15、如何指定子程序的传递参数是动态的,对动态参数有哪些规定?
7.16、编写一个子程序,其功能是把其所有参数数值之和存入AX中,每个参数都数16位二进制数,但个数不定。
7.17、子程序的参数传递有传值和传地址之分,在汇编语言中,如何实现传地址?请举例说明。
7.18、在高级语言中,子程序可定义其局部变量,在汇编语言中,能定义其局部变量吗?若能,请举例说明。
7.19、使用.REPEAT和.UNTIL语句编写一个子程序,其功能是从DS:SI开始的内存单元中查找是否有与AL相同的数值。若找不到,CF=0,否则,CF=1,且SI指向所找到位置。假设查找区域以0为结束标志。
7.20、用.REPEAT和.UNTILCXZ语句编写一个子程序,用以在内存缓冲区中填入00H。内存缓冲区的首址及长度分别由DS:SI和CX来确定。
7.21、用.WHILE和.ENDW语句实现7.16和7.17的功能。
7.22、在程序模块中,伪指令PUBLIC和EXTRN的作用是什么?
7.23、在C语言程序中,存在关键字EXTERN,它与汇编语言中EXTRN的作用相似吗?
7.24、如何创建和维护自己的子程序库?使用子程序库有何好处?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -