Assembly 比较负整数时的混淆
我已经开始学习汇编,对于一个示例程序我有一些困难 我编写了一个宏,可以在数组中找到最小值:Assembly 比较负整数时的混淆,assembly,x86-64,negative-number,yasm,Assembly,X86 64,Negative Number,Yasm,我已经开始学习汇编,对于一个示例程序我有一些困难 我编写了一个宏,可以在数组中找到最小值: %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
%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
寄存器中,我有十六进制值0xfffffd
和十进制值4294967293
但是,如果我使用计算器0xFFFFFFFD
实际上是-3
,这是正确的值
你认为我的程序正确吗
提前感谢您的回答。这是不正确的,尽管使用较小的值进行测试会隐藏错误 数组元素的类型不一致。它们是用
dd
定义的,地址计算与之一致(使用4*索引
)<代码>cmp eax,[rbx+r12*4]也与此一致。但是movsx eax,字[rbx+r12*4]
不是,现在突然该元素的上16位没有被使用
通过编写mov eax、[rbx+r12*4]
可以很容易地解决这个问题
顺便说一句,您通常不应该使用
循环
,它是在大多数现代处理器上使用的 这是不正确的,尽管用小值测试它会隐藏错误
数组元素的类型不一致。它们是用dd
定义的,地址计算与之一致(使用4*索引
)<代码>cmp eax,[rbx+r12*4]也与此一致。但是movsx eax,字[rbx+r12*4]
不是,现在突然该元素的上16位没有被使用
通过编写mov eax、[rbx+r12*4]
可以很容易地解决这个问题
顺便说一句,您通常不应该使用
循环
,它是在大多数现代处理器上使用的 0xFFFFFD
是32位值1111_1111_1111_1111_1111_1111_1111_1101
,这可能是CPU物理内部最接近的隐喻(32个具有不同电流水平或磁极编码逻辑值0或1的单元)
无论您将其解释为-3
或4294967293
或完全不同的内容(比如32个独立的真/假值),都取决于代码,代码使用该值
负整数通常使用2的补码编码,这是您用-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寄存器中设置足够的标志,使后面的条件分支在这两种情况下都成为可能。0xFFFFFFFD
是32位值1111_1111_1111_1111_1111_1111_1101
,这可能是对CPU具有物理内部(32个具有不同电流水平或磁极编码逻辑值0或1的单元)
无论您将其解释为-3
或4294967293
或完全不同的内容(比如32个独立的真/假值),都取决于代码,代码使用该值
负整数通常使用2的补码编码,这是您用-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位值,并没有其他附加信息,比如某种类型,等等