为什么在汇编语言中执行操作时必须在字符串中添加-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], '$'