今天的任务是对字符串实现增、删、改、查等等操作
假设数据空间总长度为M,已经占去部分为N。
增操作有两种:
1、在数据末尾添加新的数据段
2、在某个字符串中间添加新的数据段。
前者很简单,不存在覆盖问题。后者需要先移动后续内容,再将新内容覆盖进去。
删操作也有两种:
1、删除末尾字符串
2、删除中间字符串
后者存在无用空白问题,需要将后续内容依次前移
改操作分为三种:
1、新的字符串长度<旧的字符串长度
2、新的字符串长度=旧的字符串长度
3、新的字符串长度>旧的字符串长度
第一类需要将后续内容前移,第三类需要提前将后续内容后移
查操作比较简单,从开始到末尾顺序筛查即可,可以设计为报多个坐标,也可以设计为只报出现总数或头一个的位置
首先是改操作,由于笔者很懒,所以并没有将后续内容依次前移,而是留下了一些内容渣渣。也没有考虑提前将内容后移,通过在readme中写明输入要求,降低了自己的工作量。
主体的main函数
STACK SEGMENT PARA STACK
DW 100H DUP(?) ;不需要栈空间顶部地址
STACK ENDS
DATA SEGMENT PARA
STRING1 DB '15061125 ZHAOZE',00H ;随意的两个字符串变量
STRING2 DB 'STRING BEFORE STRCPY',00H
DATA ENDS
CODE SEGMENT PARA
ASSUME CS:CODE, DS:DATA, SS:STACK, ES:DATA
MAIN PROC FAR
MOV AX,DATA
MOV DS,AX
MOV ES,AX ;ES用于STOSB操作
CALL DISP_STR ;无参数调用,所以不用参数压栈
MOV DX,OFFSET STRING2
PUSH DX ;第二个参数,字符串2的地址偏移
MOV DX,OFFSET STRING1
PUSH DX ;第一个参数,字符串1的地址偏移
CALL FAR PTR STRCPY ;调用修改函数
CALL DISP_STR ;调用输出函数
MOV AX,4C00H ;结束
INT 21H
输出函数就不贴代码了,仅贴修改函数的代码。由于已经降低了很多难度,所以逻辑看起来非常的简单
STRCPY PROC FAR
PUSH BP ;把此时的BP值压栈
MOV BP,SP ;将SP的值传给BP:由于后续有栈操作
; SP的值可能改变,但是参数位置又是固定的
; 所以用BP过渡
PUSH DI ;函数中要用到临时寄存器DI,SI,将初值保留
PUSH SI
MOV SI,[BP+6] ;BP=初始时SP位置。BP+6是第一个参数
MOV DI,[BP+8] ;BP+8是第二个参数
CLD ;时钟置1
STRC:
LODSB
STOSB
CMP AL,0
JNZ STRC
POP SI ;还原现场
POP DI
POP BP
RET 4 ;栈空间还原,返回2N,N为参数个数
STRCPY ENDP
这次的代码与上次相比,涉及到了两个新的知识点
第一个是LODSB和STOSB,
对应的修改在main函数中也有体现,MOV ES,AX。即将额外段也指向DATA
LODSB = 从DS:[SI]地址处取一个字节,存入到AL中,并且SI++
STOSB = 将AL中的内容,存到ES:[DI]所指向的那个字节,且DI++
需要注意的是,ES:[DI],指的是以ES为基地址,偏移量为DI的位置,所以需要设置ES的初始值
CLD表示的是时钟为1,每次SI和DI自增的值为1而不是2
第二个是参数压栈。了解过栈知识,并实现过编译器的同学应该知道,参数压栈的时候应该逆序压栈,如此才能正序调用。
堆栈 | SP的值 |
---|---|
... | |
BP | 2K-10 |
跳转前IP地址 | 2K-8 |
跳转前CS值(段内调用不需要存入) | 2K-6 |
参数1 | 2K-4 |
参数2 | 2K-2 |
... | 2K |
当我们执行压栈操作的时候,此时SP指向的是2K
压入参数2,SP指向2K-2
压入参数1,SP指向2K-4
执行CALL指令,压入CS和IP,SP指向2K-8
进入函数内部,压入BP,SP指向2K-10
将此时SP的值赋给BP,BP=2K-10
则BP+6 = 2K-4,取出参数1
BP+8 = 2K-2,取出参数2
RET时,除了自动释放的4个栈空间,还需要释放参数所占空间2N
网友评论