这是一个简单的问题,但它让我头晕目眩。我需要将一串字符(输入为负十进制数)转换为无符号整数。 rdi寄存器保存要转换的字符串。 rax寄存器将保存结果。
public bool IsInBetween(string target, string start, string end)
{
DateTime targetDate = DateTime.Parse(target);
DateTime startDate = DateTime.Parse(start);
DateTime endDate = DateTime.Parse(end);
return startDate <= targetDate && targetDate <= endDate;
}
我需要迭代每个字符,我试图通过使用rsi寄存器来使用它。但每次我尝试这个时,都会出现分段错误。
无效的组合错误。我知道这是因为寄存器的大小不同,但我不知道如何继续将转换后的ascii值添加回rax。
这里有一个类似的问题帮助我更好地理解了这个过程,但是我遇到了障碍: Convert string to int. x86 32 bit Assembler using Nasm
答案 0 :(得分:2)
我需要迭代每个字符,我试图通过使用rsi寄存器来使用它。但每次尝试这个时,我都会遇到分段错误。
根据您显示的代码以及RDI
保存字符串开头地址的语句,我可以看到为什么您会在该负载中遇到分段错误的几个不同原因
问题可能是RDI
包含8个字符的ASCII字符串(按值传递),而不是包含字符串的内存位置的地址(通过引用传递)?
另一个更可能的可能性是它在循环的前几次迭代中工作正常,但是你开始尝试读取超过字符串的结尾,因为你没有正确地终止循环。您显示的代码中没有dtoi_end
标签,也没有实际跳转到convert_end
标签的位置。这些应该是相同的标签吗?如果我传入字符串“-2”会发生什么?你的循环何时终止?看起来我不喜欢!
您需要某种方式来指示整个字符串已被处理。有几种常见的方法。一种是在字符串的末尾使用一个sentinel终结符,就像C与ASCII NUL字符一样。在循环内部,您将检查正在处理的字符是否为0(NUL),如果是,则跳出循环。另一种选择是将字符串的长度作为函数的附加参数传递,就像Pascal对count-length字符串一样。然后,你将在循环内部进行测试,检查你是否已经处理了足够多的字符,如果是,则跳出循环。
我会尽量不要过于讲究这个问题,但你应该能够通过使用调试器来自己检测这个问题。逐行执行代码,观察变量/寄存器的值,并确保您了解正在发生的事情。这基本上就是我在分析你的代码时所做的,除了我用我的头作为调试器,在我自己的脑海中“执行”代码。但是,让计算机更容易(并且不易出错),这也就是发明调试器的原因。如果您的代码无效,并且您没有在调试器中逐行执行,那么您还没有努力工作来自己解决问题。实际上,单步执行您编写的每个函数是一个很好的习惯,因为(A)它将确保您理解您所写内容的逻辑,以及(B)它我会帮你找到虫子。
无效的组合错误。我知道这是因为寄存器的大小不同,但是如果继续将转换后的ascii值添加回rax,我很遗憾。
你必须使尺寸匹配。您可以执行add al, dl
,但之后您将结果限制为8位BYTE。这可能不是你想要的。因此,您需要将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, dl
和xor 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
(警告:这个逻辑未经测试!我只是以更优化的方式重写了现有代码,没有错误。)