【问题标题】:x86 NASM convert string of chars to integerx86 NASM 将字符串转换为整数
【发布时间】:2017-09-16 11:07:18
【问题描述】:

这是一个简单的问题,但它让我头晕目眩。我需要将一串字符(输入为负十进制数)转换为无符号整数。 rdi 寄存器保存要转换的字符串。 rax 寄存器将保存结果。

    xor rsi, rsi
    xor rax, rax
    xor dl, dl
    xor rdx, rdx
convert:
    mov dl, [rdi+rsi]    ;+rsi causes segmentation fault

    cmp dl, "-"
    jz  increment

    cmp dl, "."
    jz  dtoi_end

    sub dl, "0"

    mov rdx, 10
    mul rdx

    add rax, dl          ;invalid combination

    inc rsi
    jmp convert

increment:
    inc rsi
    jmp convert

convert_end:
    ret
  1. 我需要遍历每个字符,我正在尝试通过使用 rsi 寄存器来使用它。但是每次我尝试这个时,我都会遇到分段错误。

  2. 无效的组合错误。我知道这是因为寄存器的大小不同,但我不知道如何继续将转换后的 ascii 值添加回 rax。

这里有一个类似的问题帮助我更好地理解了这个过程,但我碰壁了: Convert string to int. x86 32 bit Assembler using Nasm

【问题讨论】:

  • DLRDX 的一部分。你对DL 所做的,你也对RDX 做,反之亦然。其次,mul rdx 取操作数和RAX,将它们相乘并将结果存储在RDX:RAX 中。这显然不是你想要的。你必须重新考虑你对寄存器的使用..
  • mov dl, [rdi+rsi] 崩溃,当您访问不应访问的内存时。 rdi 指向哪里?我个人不会添加 rdi 和 rsi,因为您将 2 个寄存器用于一个目的。初始化 rsi 以指向“字符串”,并使用 mov dl,[rsi] + inc rsi 加载字节

标签: assembly nasm x86-64


【解决方案1】:

我需要遍历每个字符,并且我正在尝试通过使用 rsi 寄存器来使用它。但是每次我尝试这个时,我都会遇到分段错误。

根据您显示的代码,以及 RDI 保存字符串开头地址的语句,我可以看到导致该负载出现分段错误的几个不同原因。

也许问题在于RDI 包含一个 8 字符的 ASCII 字符串(按值传递),而不是包含该字符串的内存位置的地址(按引用传递)?

另一个更可能的可能性是它在循环的前几次迭代中运行良好,但随后您开始尝试读取字符串的末尾,因为您没有正确终止循环。您显示的代码中没有dtoi_end 标签,也没有您实际跳转到convert_end 标签的地方。这些应该是同一个标签吗?如果我传入字符串“-2”会发生什么?您的循环何时终止?在我看来它不会!

您需要某种方式来表明整个字符串已被处理。有几种常用的方法。一种是在字符串末尾使用标记终止符,就像 C 对 ASCII NUL 字符所做的那样。在循环内部,您将检查正在处理的字符是否为 0 (NUL),如果是,则跳出循环。另一种选择是将字符串的长度作为附加参数传递给函数,就像 Pascal 对计数长度字符串所做的那样。然后,您将在循环内进行测试,检查您是否已经处理了足够多的字符,如果是,则跳出循环。

我会尽量不要太说教,但您应该能够通过使用调试器自己检测到这个问题。逐行执行代码,查看变量/寄存器的值,并确保您了解正在发生的事情。这基本上是我在分析你的代码时所做的,除了我用我的头作为调试器,在我自己的脑海中“执行”代码。不过,让计算机来做这件事要容易得多(而且不容易出错),这就是发明调试器的原因。如果您的代码不工作,并且您没有在调试器中逐行执行它,那么您还没有足够努力地自己解决问题。事实上,单步执行你编写的每个函数是一个很好的习惯,因为 (A) 它会确保你理解你所写内容的逻辑,并且 (B) 它'将帮助您找到错误。

无效的组合错误。我知道这是因为寄存器的大小不同,但我不知道如何继续将转换后的 ascii 值添加回 rax。

您必须使尺寸匹配。您可以执行add al, dl,但您会将结果限制为 8 位字节。这可能不是你想要的。所以,你需要把dl 变成一个64 位的QWORD,比如rax。最明显的方法是使用MOVZX 指令,该指令执行零扩展。换句话说,它将值“扩展”到更大的大小,用 0 填充高位。这就是您想要的无符号值。对于有符号值,您需要进行符号感知扩展(即考虑符号位),为此,您将使用MOVSX 指令。

在代码中:

movzx  rdx, dl
add    rax, rdx

请注意,正如其中一位评论者所指出的,DL 只是RDX 寄存器的最低 8 位:

| 63 - 32 | 31 - 16 | 15 - 8 | 7 - 0 |
--------------------------------------
                    |   DH   |   DL  |
--------------------------------------
          |           EDX            |
--------------------------------------
|                 RDX                |

因此,xor dl, dlxor rdx, rdx 是多余的。后者完成了前者。另外,每次修改dl,实际上都是在修改rdx的最低8位,这会导致结果不正确。提示,提示:这是您可以通过使用调试器单步执行的其他操作(尽管您可能不明白为什么!)。

而且,xor rdx, rdx 完全没有必要!您可以通过 xor edx, edx 完成相同的任务,more efficiently


只是为了好玩,这是代码的一种可能实现:

; Parameters: RDI == address of start of character string
;             RCX == number of characters in string
; Clobbers:   RDX, RSI
; Returns:    result is in RAX

    xor   esi, esi

convert:
    ; See if we've done enough characters by checking the length of the string
    ; against our current index.
    cmp   rsi, rcx
    jge   convert_end

    ; Get the next character from the string.
    mov   dl, BYTE [rdi + rsi]

    cmp   dl, "-"
    je    increment

    cmp   dl, "."
    je    convert_end

    ; Efficient way to multiply by 10.
    ; (Faster and less difficult to write than the MUL instruction.)
    add   rax, rax
    lea   rax, [4 * rax + rax]

    sub   dl, "0"
    movzx rdx, dl
    add   rax, rdx

    ; (fall through to increment---no reason for redundant instructions!)

increment:
    inc   rsi            ; increment index/counter
    jmp   convert        ; keep looping

convert_end:
    ret

(警告:此逻辑未经测试!我只是以更优化的方式重写了您现有的代码,没有错误。)

【讨论】:

  • 我正在寻找 asm 中 atoi 的规范 dup 目标。这接近一个好的目标,但对"-" 进行了奇怪的检查。它还具有 OP 的笨重循环结构(底部的 jmp),并且您使用了单独的 movzx 而不是将加载设为 movzx。例如movzx edx, byte [rdi+rsi]/sub edx, "0"/cmp dl, "9"/ja .maybe_exit_unless_it_was_minus/lea rax, [4*rax + rax]/lea rax, [rdx + 2*rax]。 (即,将新数字的加法乘以 10,同时使用 2 个 LEA)。
猜你喜欢
  • 2013-03-17
  • 2013-10-27
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
  • 2012-02-21
  • 2010-12-31
相关资源
最近更新 更多