例10.13 编写一个程序用动态链表存储20,19,……,1,并用遍历链表的方法来显示每个结点的数值。
解:为了便于理解本例程序的执行过程和所构成的动态链表,我们把该链表的构成过程表示成图10.1所示。
图10.1 动态链表数据结构示意图 |
||||
.MODEL SMALL, C | ||||
.DATA | ||||
Head |
DW 0 | ;链表的头指针,初值为空 | ||
PT |
DW ? | ;临时链表指针:当前的链表尾 | ||
Buff |
DB ?, ?, "$" | ;存放输出结果的缓冲区 | ||
CRLF |
DB 0DH, 0AH, "$" | ;回车、换行信息 | ||
.CODE | ||||
DispMsg | PROC USES AX DX, Msg:PTR BYTE | ;显示字符串Msg | ||
…… | ;参见例10.7 | |||
DispMsg | ENDP | |||
.STARTUP | ||||
MOV | CX, 20 | |||
.REPEAT | ||||
MOV |
BX, 1 | ;申请内存的字节数=BX×16 | ||
MOV |
AH, 48H | |||
INT |
21H | ;申请内存的空间 | ||
.BREAK .IF CARRY? |
;申请内存失败 | |||
MOV |
ES, AX | ;AX=申请内存的段地址 | ||
MOV |
WORD PTR ES:[0], CX | ;给第一个字赋值 | ||
MOV |
WORD PTR ES:[2], 0 | ;给第二个字置“空”(NULL) | ||
.IF Head==0 |
||||
MOV Head, ES |
;保存链表的头指针 | |||
.ELSE |
||||
PUSH DS |
||||
MOV DS, PT |
||||
MOV WORD PTR DS:[2], ES |
;把当前申请到的结点链到链表中 | |||
POP DS |
||||
.ENDIF |
||||
MOV |
PT, ES | ;当前结点向后移 | ||
.UNTILCXZ | ||||
MOV | BX, Head | ;从头开始遍历链表 | ||
.WHILE (BX!=0) | ||||
MOV |
ES, BX | ;第一个结点的段地址 | ||
MOV |
AX, ES:[0] | ;取第一个结点的数据字段值 | ||
MOV |
DL, 10 | |||
IDIV |
DL | |||
ADD |
AX, 3030H | ;把结点的值转换成字符 | ||
LEA |
BX, BUFF | |||
MOV |
[BX], 2020H | ;把字符缓冲区清空 | ||
.IF AL>30H |
;若十位有非零值,则存储其字符 | |||
MOV [BX], AL |
||||
INC BX |
||||
.ENDIF |
||||
MOV |
[BX], AH | ;存储个位字符 | ||
INVOKE |
DispMsg, ADDR Buff | ;显示结点数据的字符串 | ||
INVOKE |
DispMsg, ADDR CRLF | ;显示回车、换行 | ||
MOV |
BX, ES:[2] | ;BX=下一个结点的段地址 | ||
MOV |
AH, 49H | ;当前结点的段地址已在ES中 | ||
INT |
21H | ;释放当前结点所占的空间 | ||
.ENDW | ||||
.EXIT 0 | ||||
END |