有/无符号数 与参数的一点的问题,各位小心 ( 积分: 50 )

  • 主题发起人 主题发起人 QSmile
  • 开始时间 开始时间
Q

QSmile

Unregistered / Unconfirmed
GUEST, unregistred user!
一个有符号与无符号数比较的问题,各位小心

一小段代码

var
n1:Integer;
n2:DWORD;
begin
n1 := -1;
n2 := DWORD(-1);
if n1 <> n2 then
ShowMessage('不相等')
else
ShowMessage('相等');

你认为这段代码运行结果是如何呢? 如果在 C 中,等效的代码一定显示 相等。
但在 Delphi 中是不相等的。 这样的错误我遇到很多次了,特别是在调用 API 时。
因为API 是用 C 写的,很多 API 说明里是 DWORD 的,在 Delphi 中被写成 Integer

还有更烦的。 API 中有些指针参数,有些在 Delphi 中是用 var 来隐式传址,有些又必须手动传地址。哎!干脆取消 var 还方便点
 
n1 := -1;
Integer(n2) := -1;
if n1 <> n2 then
ShowMessage('不相等')
else
ShowMessage('相等');
你在试试。另外var本身就是传址的。
 
看一下结果就明白了
var
n1:Integer;
n2:DWORD;
begin
n1 := -1;
n2 := DWORD(-1);
ShowMessage(inttostr(n1));
ShowMessage(inttostr(n2));
 
n1 := -1;
Integer(n2) := -1;
if n1 <> n2 then
ShowMessage('不相等')
else
ShowMessage('相等');


结果还是一样,我用D7 试的

---------------
另外var本身就是传址的。

我知道 var 就是传址,但很多 API 定义成有些要手动传址,有些是自动传址,太乱了。这样反而对 Delphi 发展不好
比如这个:
function WSAGetOverlappedResult( s : TSocket;
lpOverlapped : LPWSAOVERLAPPED;
lpcbTransfer : LPDWORD;
fWait : BOOL;
var lpdwFlags : DWORD ) : WordBool;

中间的 cbTransfer 要手动传地址,而后面那个 flags 又是自动传地址的

不统一
 
这个跟编译器有关,在你的例子中, N1 和 N2 都被转换成 $FFFFFFFF 并放在寄存器中.
问题就在 if n1 <> n2 then 这个比较中, delphi 会出现提示信息告诉你有符号数和无符号数比较会自动转换.所以 -1 就会被转换为 0, 具体可以看以下代码.

push ebx
or ecx, FFFFFFFF
or ebx, FFFFFFFF
mov eax, ecx
cdq
push edx
push eax
mov eax, ebx
xor edx, edx
cmp edx, [esp+4]
jnz L012
cmp eax, [esp]
L012:
pop edx
pop eax
je L019
mov eax, 00452B10
call 00427B8C
pop ebx
retn
L019:
mov eax, 00452B20
call 00427B8C
pop ebx
retn

想避免这个问题,就最好不要作有无符号数之间的比较,这个应该是 pascal 语法严谨的体现(相对你说 c 没问题), 改成 if DWORD(n1) <> n2 then 的话代码如下

or eax, FFFFFFFF
or edx, FFFFFFFF
cmp edx, eax
je L007
mov eax, 00452AF8
call 00427B8C
retn
L007:
mov eax, 00452B08
call 00427B8C
retn
 
我知道是什么原理,我只是提出来让各位小心点而已
 
这样写还有一个明显的缺点就是使delphi的优化失效,代码明显大了很多.
 
多人接受答案了。
 
后退
顶部