【问题标题】:How to reverse and print a string in assembly language如何用汇编语言反转和打印字符串
【发布时间】:2015-11-13 04:18:12
【问题描述】:

所以我的任务是用汇编代码编写一个程序,它可以发出语句,接收用户输入的字符串。打印该字符串,然后使用 cpu 堆栈将其反转并再次打印。这就是我迄今为止所拥有的。

INCLUDE Irvine32.inc

.data
buffer byte 20 DUP(0)
byteCount DWORD ?

Question  byte  "Please enter your name."  ,0
Greeting byte   "Hello " ,0
Statement byte " Here is your name backwards"

.code
main proc


mov edx , OFFSET Question
call WriteString
call CRLF
Call CRLF

mov edx, OFFSET buffer
mov Ecx, SIZEOF buffer
call ReadString

push edx
mov EDX ,OFFSET greeting
Call WriteString
pop edx
call WriteString
Call CRLF
Call CRLF

正如您所看到的,这成功地接受了用户输入的输入并显示它,但我真的很难尝试扭转它。

我在这里尝试了这些,这些是我从书中关于反转字符串的一章中复制的。

; Push the name on the stack.

mov ecx,nameSize
mov  esi,0

L1: movzx eax,aName[esi]    ; get character
push eax                ; push on stack
inc  esi
loop L1

; Pop the name from the stack in reverse
; and store it in the aName array.

mov  ecx,nameSize
mov  esi,0

L2: pop  eax                ; get character
mov  aName[esi],al      ; store in string
inc  esi
loop L2

Invoke ExitProcess,0
main endp
end main

但我什么也没得到。

上面写着“你好,(这里是你的名字)”

上面写着“这是你的名字倒过来”

我尝试了几乎所有我能想到的不同的化身,但无济于事。我在我的“字符串”的末尾

【问题讨论】:

  • 好的,反转字符串将是您作业的主要部分。您能否向我们展示/告诉我们您尝试过但没有成功的事情?
  • 您添加的代码甚至没有使用相同的变量名称。您是否真的尝试将该代码实际集成到您的程序中?
  • 是的,我更改了所有引用,以便它可以网格化。
  • 我这边所有变量名都正确对应
  • 我创建了一个答案,可以帮助您在 Windows 上开始使用 MASM/Irvine32 库。变量不一样。您的反转代码使用 aName 并且您编写的原始代码使用 buffer 。您的原始代码不会保存读取的字符数(用户名中的字符数)。变量 nameSize 未出现在您的原始代码等中。

标签: assembly x86 reverse masm irvine32


【解决方案1】:

这违背了我的更好判断,因为用于反转的代码的 sn-p 甚至还没有集成到原始发布者创建的代码中。变量名称不同。代码的快速而肮脏的集成是创建一个变量 nameSize,它保存从对 ReadString 的调用中读取的字符数。 ReadString(Irvine32 库的一部分)返回在寄存器 EAX 中读取的字符数。

.data 部分添加变量:

nameSize  DWORD ? 

ReadString 后将EAX 寄存器的内容移动到nameSize。这段代码:

mov edx, OFFSET buffer
mov Ecx, SIZEOF buffer
call ReadString

应该是:

mov edx, OFFSET buffer
mov Ecx, SIZEOF buffer
call ReadString
mov nameSize, eax       ; EAX contains number of characters read into buffer

在用于反转代码的代码 sn-p 中,删除程序结束等底部的行。这些不是必需的,因为我们将在原始代码中执行此操作。

Invoke ExitProcess,0
main endp
end main

在字符串反转代码中,我们看到变量 aName 的任何地方都将其更改为 buffer,因为这是我们放置用户名的地方。将该代码放入我们的程序中,并在最后使用 WriteString 打印反转的缓冲区。代码可能类似于:

INCLUDE Irvine32.inc

.data
buffer byte 20 DUP(0)
byteCount DWORD ?
nameSize  DWORD ?

Question  byte  "Please enter your name."  ,0
Greeting byte   "Hello " ,0
Statement byte " Here is your name backwards"

.code
main proc

    mov edx , OFFSET Question
    call WriteString
    call CRLF
    Call CRLF

    mov edx, OFFSET buffer
    mov Ecx, SIZEOF buffer
    call ReadString
    mov nameSize, eax

    push edx
    mov EDX ,OFFSET greeting
    Call WriteString
    pop edx
    call WriteString
    Call CRLF
    Call CRLF

    mov  ecx,nameSize
    mov  esi,0

L1: movzx eax,buffer[esi]    ; get character
    push eax                ; push on stack
    inc  esi
    loop L1

    ; Pop the name from the stack in reverse
    ; and store it in the aName array.

    mov  ecx,nameSize
    mov  esi,0

L2: pop  eax                ; get character
    mov  buffer[esi],al      ; store in string
    inc  esi
    loop L2


    mov EDX ,OFFSET buffer
    call WriteString         ; Write the reversed string that is now in buffer

    exit
main ENDP
END

如果您遇到链接错误,您可能没有在所有必备库中进行链接。尝试将这些行添加到程序的顶部:

INCLUDE Irvine32.inc
INCLUDELIB Irvine32.lib
INCLUDELIB user32.lib
INCLUDELIB kernel32.lib

我应该指出,如果您不介意破坏原始字符串,这是一种非常低效的反转字符串的方法。通过将字符串反转到位,可以在堆栈上没有辅助缓冲区的情况下完成。

【讨论】:

  • 通过压入堆栈来反转 16、32 或 64 位整数的序列实际上是一种聪明的做法。然后它就在堆栈上,以相反的顺序,从[esp][esp + count * size]。再次弹出它是荒谬的。而且由于没有办法将单个字节压入堆栈(push [m8] 或其他东西),你说得对,这个想法对字符串来说很糟糕。如果您一次加载 2、4B 或 8B,并且在推送之前使用 bswap 来反转这些字节,它将起作用。 (或者,movbe 即时加载交换。(Atom、Haswell 和 Jaguar))。
  • @PeterCordes 这是 OP 尝试集成的代码(在更新后的问题中),并且需要帮助来完成基础知识(家庭作业)。问题是/是边界投票结束,具体取决于您要如何定义 reasonable effort 。如果我提供了答案,我想从他们的机构获得荣誉学位;-)
  • + Michael Petch 谢谢,但我什至无法编译您的代码。我在构建它时遇到两个错误。 1 未解析的外部和未解析的外部符号_mainCRTStartup
  • @JonDeal 那些错误不在编译中,那些错误在链接阶段。从外观上看,您并没有链接到所有必需的库。我只是使用了你给我的代码。我在答案的底部添加了一小部分,可以解决链接问题。我所知道的是程序确实在这里编译和运行。
  • +迈克尔·佩奇。嗯仍然没有链接我尝试了所有这些包含语句,但我仍然遇到这两个错误。我真的很茫然,为什么它不起作用?你没有写任何我以前没用过的代码。
【解决方案2】:

在高层次上:

  • 分配一个“反向”和“文本”缓冲区
  • 将字符串读入'text'
  • 在文本末尾创建一个指针,将每个字符复制到开头,分别递减和递增。
  • 打印新的“反向”缓冲区。

在不分配新缓冲区的情况下执行此操作也是可能的,但一般应避免,因为调用系统调用的成本(您需要在每个字符之后执行此操作)

section .data

prompt      db  "Please enter your name: ", 10
length      equ $ - prompt
text        times 255 db 0
buffer      times 255 db 0

Enter your text
section .text
global main
main:
    mov  rax, 1
    mov  rdi, 1
    mov  rsi, prompt
    mov  rdx, length
    syscall

    mov  rax, 0
    mov  rdi, 0
    mov  rsi, text
    syscall

    mov  rcx, rax  ; rcx will be the character counter.
    mov  rsi, text ; a pointer to the current character. Start from the beginning.
    add  rsi, rcx  
    dec  rsi       ; Remember the 0-index
    mov  rdi, buffer

;; This subroutine is also SUB-optimal if your teacher demands
;; performance, look into the advantages of `lea` and a simple 
;; rep;scas loop as well.
process_loop:
    mov bl, [rsi]               ; Now copy from back to front
    mov [rdi], bl
    inc rdi
    dec rsi
    dec rax
    jnz process_loop

    mov  rax, 1                 ; And print the string
      mov  rdi, 1
      mov  rsi, buffer
      mov  rdx, rcx
      syscall

exit:
    mov     rax, 60
    mov     rdi, 0
    syscall

【讨论】:

  • 这是使用 NASM 语法的 64 位 Linux 代码(显然使用 32 位系统调用)。原始海报使用 MASM 语法在 Windows 上使用 32 位代码。我不认为这在 OP 使用的环境中提供了很多答案。
  • 我同意并注意到,我也不认为给最初的发帖人提供一个他们可以抄写作业的准确答案是非常慷慨的。不用说,移植这个逻辑是微不足道的,并且会迫使用户熟悉这些概念(并可能在 MASM 上学习语义,如 byte ptr
  • 你有没有看到我用 这违背了我更好的判断作为我的答案的序言。我不愿意提供的是更有效的方法,我希望教授能给出更好的分数。这就是为什么在我的结语中我对此发表评论;)用户有一个有效的反转功能只需要整合。不像我写了很多新代码(3行)
  • 还值得注意的是,他在问题中说,分配要求他使用“CPU 堆栈”来反转它,虽然这非常简单,但我们都没有这样做。 (如push/pop 字符远)
  • 实际上他提供的代码(并且我重用了)实际上使用堆栈作为辅助缓冲区。您将观察到每个字符都在一个循环中通过push eax 的过程将字符串放入堆栈。另一个循环使用pop eax
猜你喜欢
  • 2017-03-01
  • 2014-06-06
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多