与比较负整数混淆

时间:2018-08-09 16:18:23

标签: assembly x86-64 negative-number yasm

我已经开始研究汇编,并且在示例程序方面有些困难。

我写了一个可以在数组中找到最小值的宏:

@Injectable

该程序成功编译,但是当我调试它(​​使用DDD)时,在%macro min 3 mov ecx, dword[%2] mov r12, 0 lea rbx, [%1] movsx eax, word[rbx+r12*4] ; inizializza il minimo con il primo elemento dell'array %%minLoop: cmp eax, [rbx+r12*4] jl %%notNewMin movsx eax, word[rbx+r12*4] %%notNewMin: inc r12 loop %%minLoop mov [%3], eax %endmacro section .data EXIT_SUCCESS equ 0 SYS_exit equ 60 list1 dd 4, 5, 2, -3, 1 len1 dd 5 min1 dd 0 section .text global _start _start: min list1, len1, min1 last: mov rax, SYS_exit ; exit mov rdi, EXIT_SUCCESS ; success syscall 寄存器中,我有一个十六进制值eax和一个十进制值0xFFFFFFFD

但是,如果我使用计算器4294967293的确是0xFFFFFFFD,那是正确的值。

您认为我的程序正确吗?

预先感谢您的回答。

2 个答案:

答案 0 :(得分:4)

这是不正确的,尽管用较小的值进行测试会隐藏该错误。

将数组的元素视为哪种类型存在不一致。它们用dd定义,并且地址计算与此一致(使用4*index)。 cmp eax, [rbx+r12*4]也与此一致。但是movsx eax, word[rbx+r12*4]不是,现在突然不使用元素的高16位。

通过写mov eax, [rbx+r12*4]可以很容易地解决此问题。

通常您不应该使用loop的方式,在大多数现代处理器上都是quite slow

答案 1 :(得分:3)

0xFFFFFFFD是32位值1111_1111_1111_1111_1111_1111_1111_1101,这可能是CPU物理内部的最接近比喻(32个具有不同电流水平的单元或编码逻辑值为0或1的磁极)。

您是否解释为-34294967293还是完全不同的东西(例如32个独立的真/假值)取决于代码,使用该值。

负整数通常使用二进制补码,您可以使用-3值进行观察。

调试器不知道您是将值解释为带符号的还是无符号的(除非您通过格式化参数指定它),因此调试器将选择一种格式并像这样显示,在您的情况下为无符号的32位值,表示您看到的是4294967293而不是-3,但按位表示这两个是相同的,并且对于add/sub/cmp/test/...之类的算术指令来说,值是相同的,仅对结果(和标志)的解释是以下代码将确定该值是“有符号”还是“无符号”。

符号本身不是编码信息的一部分,或者有时高位被视为“符号”位,因为所有负值都设置了高位,但这就是为什么带符号的8位值只能存储值的原因-128 .. + 127,而无符号的8位值可以存储值0 .. + 255(即两种解释都恰好覆盖256个不同的值,因为8位可以产生256个不同的0/1模式组合,但是带符号的解释“开始“在“ 0x80 = -128”处,而无符号解释“从”在“ 0x00 = 0”处开始,并且0x80已被解释为+128。但是两种解释都仅使用8位值,因此没有其他附加信息,例如某种类型等。

例如

cmp    eax, ebx   ; check if eax is bigger than ebx
; now if the values were meant as unsigned, then use "ja" branch
ja     eax_is_bigger_as_unsigned
; but if you meant the values as signed, then you should use "jg" (testing different flags)
jg     eax_is_bigger_as_signed

因此cmp本身并不关心您如何解释该位模式,它将在EFLAGS寄存器中设置足够的标志,以使这两种情况都可以进行以后的条件分支。