混淆比较负整数
Confusion with comparing negative integers
我已经开始学习汇编了,但我对示例程序有些困难。
我写了一个宏来找到数组中的最小值:
%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
这个程序成功编译,但是当我调试它(使用 DDD)时,在 eax
寄存器中我有十六进制值 0xFFFFFFFD
和十进制值 4294967293
。
但是,如果我使用计算器,0xFFFFFFFD
确实是 -3
,这是正确的值。
你认为我的程序正确吗?
提前感谢您的回答。
这是不正确的,虽然用小值测试它会隐藏错误。
数组元素的类型处理不一致。它们被定义为dd
,地址计算与之一致(使用4*index
)。 cmp eax, [rbx+r12*4]
也符合这一点。但是movsx eax, word[rbx+r12*4]
不是,现在突然用不到元素的高16位
这可以很容易地通过写 mov eax, [rbx+r12*4]
来解决。
顺便说一下,您通常不应该使用 loop
,它在大多数现代处理器上是 quite slow。
0xFFFFFFFD
是 32 位值 1111_1111_1111_1111_1111_1111_1111_1101
,这可能是最接近 CPU 物理内部的隐喻(32 个具有不同电流水平或磁极编码逻辑的单元格值 0 或 1).
无论您将解释为-3
还是4294967293
或完全不同的东西(比如32个独立的true/false值)取决于代码,即使用值。
负整数通常使用二进制补码编码,您正在用 -3
值观察。
调试器不知道您是将值解释为有符号还是无符号(除非您通过格式化参数指定它),因此它会选择一种格式并像那样显示,在您的情况下为无符号 32 位值,这意味着您看到的是 4294967293
而不是 -3
,但是这两个按位是相同的,而且对于像 add/sub/cmp/test/...
这样的算术指令,该值也是相同的,只是结果(和标志)的解释通过以下代码将决定值是 "signed" 还是 "unsigned".
符号本身不是编码信息的一部分,或者有时最高位被视为 "sign" 位,因为所有负值都设置了最高位,但这就是为什么有符号 8 位值的原因只能存储值 -128..+127,而无符号 8 位值可以存储值 0..+255(即两种解释恰好涵盖 256 个不同的值,因为 8 位可以产生 0/1 模式的 256 种不同组合,但是有符号解释 "starts" 在“0x80 = -128”,而无符号解释 "starts" 在“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 寄存器中设置足够的标志,使两种情况下的条件分支成为可能。
我已经开始学习汇编了,但我对示例程序有些困难。
我写了一个宏来找到数组中的最小值:
%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
这个程序成功编译,但是当我调试它(使用 DDD)时,在 eax
寄存器中我有十六进制值 0xFFFFFFFD
和十进制值 4294967293
。
但是,如果我使用计算器,0xFFFFFFFD
确实是 -3
,这是正确的值。
你认为我的程序正确吗?
提前感谢您的回答。
这是不正确的,虽然用小值测试它会隐藏错误。
数组元素的类型处理不一致。它们被定义为dd
,地址计算与之一致(使用4*index
)。 cmp eax, [rbx+r12*4]
也符合这一点。但是movsx eax, word[rbx+r12*4]
不是,现在突然用不到元素的高16位
这可以很容易地通过写 mov eax, [rbx+r12*4]
来解决。
顺便说一下,您通常不应该使用 loop
,它在大多数现代处理器上是 quite slow。
0xFFFFFFFD
是 32 位值 1111_1111_1111_1111_1111_1111_1111_1101
,这可能是最接近 CPU 物理内部的隐喻(32 个具有不同电流水平或磁极编码逻辑的单元格值 0 或 1).
无论您将解释为-3
还是4294967293
或完全不同的东西(比如32个独立的true/false值)取决于代码,即使用值。
负整数通常使用二进制补码编码,您正在用 -3
值观察。
调试器不知道您是将值解释为有符号还是无符号(除非您通过格式化参数指定它),因此它会选择一种格式并像那样显示,在您的情况下为无符号 32 位值,这意味着您看到的是 4294967293
而不是 -3
,但是这两个按位是相同的,而且对于像 add/sub/cmp/test/...
这样的算术指令,该值也是相同的,只是结果(和标志)的解释通过以下代码将决定值是 "signed" 还是 "unsigned".
符号本身不是编码信息的一部分,或者有时最高位被视为 "sign" 位,因为所有负值都设置了最高位,但这就是为什么有符号 8 位值的原因只能存储值 -128..+127,而无符号 8 位值可以存储值 0..+255(即两种解释恰好涵盖 256 个不同的值,因为 8 位可以产生 0/1 模式的 256 种不同组合,但是有符号解释 "starts" 在“0x80 = -128”,而无符号解释 "starts" 在“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 寄存器中设置足够的标志,使两种情况下的条件分支成为可能。