为什么在汇编语言中执行操作时必须在字符串中添加-2?
Why do we have to add -2 to a string when performing operation in assembly language?
Data Segment
str1 db 'MADAME','$'
strlen1 dw $-str1 ;calculating the length of the string
strrev db 20 dup(' ')
s1 db 'String is:','$'
NEWLINE DB 10,13,"$"
str_palin db 'String is Palindrome.','$'
str_not_palin db 'String is not Palindrome.','$'
Data Ends
Code Segment
Assume cs:code, ds:data
Begin:
mov ax, data
mov ds, ax
mov es, ax
mov cx, strlen1
add cx, -2
lea si, str1
lea di, strrev
add si, strlen1
add si, -2
mov ah, 09h
lea dx, s1
int 21h
mov ah, 09h
lea dx, str1
int 21h
MOV AH,09H
LEA DX,NEWLINE
INT 21H
L1:
mov al, [si]
mov [di], al
dec si
inc di
loop L1
mov al, [si]
mov [di], al
inc di
mov dl, '$'
mov [di], dl
mov cx, strlen1
Palin_Check:
lea si, str1
lea di, strrev
repe cmpsb
jne Not_Palin
Palin:
mov ah, 09h
lea dx, str_palin
int 21h
jmp Exit
Not_Palin:
mov ah, 09h
lea dx, str_not_palin
int 21h
Exit:
mov ax, 4c00h
int 21h
Code Ends
End Begin
第一次加-2 (add cx, -2
)
考虑
mov cx, strlen1
add cx, -2 <-- Can be avoided totally
还有
L1:
mov al, [si]
mov [di], al
dec si
inc di
loop L1
mov al, [si] <-- Should stay inside the loop
mov [di], al <-- Should stay inside the loop
inc di <-- Should stay inside the loop
由于 strlen1 的定义方式 (strlen1 dw $-str1
) add cx, -2
(为什么这不只是 sub cx, 2
?)不给出字符串的正确长度。你得到的 1 太少了。后来因为这个,你的 L1 循环必须附加 3 个额外的指令!
第二次加-2 (add si, -2
)
lea si, str1
add si, strlen1
add si, -2
又一次,为什么更喜欢 add si, -2
而不是更具可读性的 sub si, 2
?
由于 strlen1 的定义方式 (strlen1 dw $-str1
),add si, strlen1
将使 SI
指向 behind 终止$字符.
减去 1 将使 SI
指向 在 终止 $ 字符,因此 behind 字符串的最后一个字符。
减去 2 将使 SI
点 在 字符串的最后一个字符。
建议
如果您重新定义 strlen1 以便它 不包含终止 $ 字符 ,则上述许多问题将不存在。当人们谈论 字符串的长度 时,他们很少在计数中包括任何终止字符。这样的字符(无论是 $ 还是零)实际上并不是字符串 的一部分
strlen1 dw $ - str1 - 1 ;Length of the string
要在上下文中查看所有内容:
mov ah, 09h
mov dx, s1
int 21h
mov ah, 09h
mov dx, str1
int 21h
mov ah, 09h
mov dx, NEWLINE
int 21h
cld ;To be absolutely safe
mov cx, strlen1 ;The improved definition! db 'MADAME','$' => 5
mov di, strrev
mov si, str1
add si, cx ;Now points behind the last character ('E')
L1:
dec si
mov al, [si]
stosb ;Equivalent to "mov [di], al" "inc di"
dec cx
jnz L1
mov byte ptr [di], '$'
请注意这些细节:
- 我已经清楚地将显示在屏幕上的代码与执行反转的代码分开了。
- 通过将
dec si
指令放在 之前 读取 [SI]
(我们称之为 预递减 ) ,在 L1. 循环开始之前,可以删除一条指令
- 我已将每个
lea
替换为 mov
。结果是一样的,但是代码少了 1 个字节。每次。
- 我用等效代码
dec cx
jnz L1
替换了慢 loop
指令。
- 我已经用一条等效指令
stosb
替换了指令对 mov [di], al
inc di
。我可以这样做是因为设置了 ES
寄存器并且我已经清除了方向标志 (DF)。你的 repe cmpsb
也依赖于 DF=0.
- 我已经用一条指令替换了编写新 $ 终止符的指令对
mov byte ptr [di], '$'
。
Data Segment
str1 db 'MADAME','$'
strlen1 dw $-str1 ;calculating the length of the string
strrev db 20 dup(' ')
s1 db 'String is:','$'
NEWLINE DB 10,13,"$"
str_palin db 'String is Palindrome.','$'
str_not_palin db 'String is not Palindrome.','$'
Data Ends
Code Segment
Assume cs:code, ds:data
Begin:
mov ax, data
mov ds, ax
mov es, ax
mov cx, strlen1
add cx, -2
lea si, str1
lea di, strrev
add si, strlen1
add si, -2
mov ah, 09h
lea dx, s1
int 21h
mov ah, 09h
lea dx, str1
int 21h
MOV AH,09H
LEA DX,NEWLINE
INT 21H
L1:
mov al, [si]
mov [di], al
dec si
inc di
loop L1
mov al, [si]
mov [di], al
inc di
mov dl, '$'
mov [di], dl
mov cx, strlen1
Palin_Check:
lea si, str1
lea di, strrev
repe cmpsb
jne Not_Palin
Palin:
mov ah, 09h
lea dx, str_palin
int 21h
jmp Exit
Not_Palin:
mov ah, 09h
lea dx, str_not_palin
int 21h
Exit:
mov ax, 4c00h
int 21h
Code Ends
End Begin
第一次加-2 (add cx, -2
)
考虑
mov cx, strlen1 add cx, -2 <-- Can be avoided totally
还有
L1: mov al, [si] mov [di], al dec si inc di loop L1 mov al, [si] <-- Should stay inside the loop mov [di], al <-- Should stay inside the loop inc di <-- Should stay inside the loop
由于 strlen1 的定义方式 (strlen1 dw $-str1
) add cx, -2
(为什么这不只是 sub cx, 2
?)不给出字符串的正确长度。你得到的 1 太少了。后来因为这个,你的 L1 循环必须附加 3 个额外的指令!
第二次加-2 (add si, -2
)
lea si, str1 add si, strlen1 add si, -2
又一次,为什么更喜欢 add si, -2
而不是更具可读性的 sub si, 2
?
由于 strlen1 的定义方式 (strlen1 dw $-str1
),add si, strlen1
将使 SI
指向 behind 终止$字符.
减去 1 将使 SI
指向 在 终止 $ 字符,因此 behind 字符串的最后一个字符。
减去 2 将使 SI
点 在 字符串的最后一个字符。
建议
如果您重新定义 strlen1 以便它 不包含终止 $ 字符 ,则上述许多问题将不存在。当人们谈论 字符串的长度 时,他们很少在计数中包括任何终止字符。这样的字符(无论是 $ 还是零)实际上并不是字符串 的一部分
strlen1 dw $ - str1 - 1 ;Length of the string
要在上下文中查看所有内容:
mov ah, 09h
mov dx, s1
int 21h
mov ah, 09h
mov dx, str1
int 21h
mov ah, 09h
mov dx, NEWLINE
int 21h
cld ;To be absolutely safe
mov cx, strlen1 ;The improved definition! db 'MADAME','$' => 5
mov di, strrev
mov si, str1
add si, cx ;Now points behind the last character ('E')
L1:
dec si
mov al, [si]
stosb ;Equivalent to "mov [di], al" "inc di"
dec cx
jnz L1
mov byte ptr [di], '$'
请注意这些细节:
- 我已经清楚地将显示在屏幕上的代码与执行反转的代码分开了。
- 通过将
dec si
指令放在 之前 读取[SI]
(我们称之为 预递减 ) ,在 L1. 循环开始之前,可以删除一条指令
- 我已将每个
lea
替换为mov
。结果是一样的,但是代码少了 1 个字节。每次。 - 我用等效代码
dec cx
jnz L1
替换了慢loop
指令。 - 我已经用一条等效指令
stosb
替换了指令对mov [di], al
inc di
。我可以这样做是因为设置了ES
寄存器并且我已经清除了方向标志 (DF)。你的repe cmpsb
也依赖于 DF=0. - 我已经用一条指令替换了编写新 $ 终止符的指令对
mov byte ptr [di], '$'
。