在Delphi 64位项目中使用in运算符

时间:2011-12-11 03:19:11

标签: delphi 64-bit delphi-xe2

我正在将一个Delphi项目移植到64位,我遇到了一行代码有IN运算符的问题。

编译器引发此错误

  

E2010不兼容的类型:'整数'和'Int64'

我写了这个示例应用来复制问题。

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;


Var
 I : Integer;
 L : Array of string;
begin
  try
     if I in [0, High(L)] then


  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.

这个代码在32位工作正常,但为什么不用Delphi XE2 64位编译?我如何解决这个问题?

* 更新*

似乎我的帖子引起了很多混淆(对不起),只是为了解释我移植的原始代码更复杂,我只是将这段代码作为样本来说明问题。原始代码使用in运算符来检查一个值(小于255)是否属于一组值(所有次要或等于255),如此

i in [0,1,3,50,60,70,80,127,High(LArray)] 

3 个答案:

答案 0 :(得分:7)

此代码无法编译,因为High函数返回的是8字节值,这不是序数值。并且In运算符只能在具有序数值的集合中使用。

仅供参考,High函数返回的结果大小因参数传递的参数而异。

检查此样本

 Writeln(SizeOf(High(Byte)));
 Writeln(SizeOf(High(Char)));
 Writeln(SizeOf(High(Word)));
 Writeln(SizeOf(High(Integer)));
 Writeln(SizeOf(High(NativeInt)));
 Writeln(SizeOf(High(TBytes)));

最后,您可以修复代码,将High函数的结果转换为整数。

 if I in [0, Integer(High(L))] then

<强>更新

检查David提供的其他信息,并且在使用in运算符检查变量值集合中的值的成员资格时要记得要非常小心。 in运算符仅检查每个元素的最低有效字节(以delphi 32位为单位)。

检查此样本

  i:=257;
  Writeln( 1 in [i]); 

这返回true,因为257的低字节是1.

在Delphi 64位中,大于255的值被删除。所以这段代码

  i:=257;
  Writeln( 1 in [i]); 

将返回false,因为它等同于

  Writeln( 1 in []); 

答案 1 :(得分:4)

RRUZ所说的非常正确。

为了增加更多解释,在64位Delphi中,动态数组索引可以是64位宽。显然需要这样做,例如,使用大型TBytes内存块时。因此high函数必须返回足够宽的类型的值来保存所有可能的索引。因此,high应用于动态数组时,返回类型为Int64的值。

一旦开始编译64位代码,in运算符就不适合您要解决的问题。虽然您可以使用RRUZ建议的演员表,但是编写像这样的代码可能更清楚

if (I=low(L)) or (I=high(L)) then

虽然in运算符提供了非常易读的代码,但我认为此处不接受对Integer的强制转换。当您第一次拥有超过high(Integer)个元素的数组时,这将为您设置一个陷阱。当发生这种情况时,带有强制转换的代码将停止工作。

但实际上问题远不止于此。 in版本的代码在您到达high(Integer)元素之前很久就会失败。事实证明,你的代码在编译时并没有真正起作用。例如,请考虑以下程序:

program WeirdSets;
{$APPTYPE CONSOLE}
uses
  SysUtils;
var
  a: array of Integer;
begin
  SetLength(a, 257);
  Writeln(BoolToStr(Length(a) in [0, Length(a)], True));
end.

您希望此程序输出True,但实际上它会输出False。相反,如果你要写

Writeln(BoolToStr(Length(a) in [0, 257], True));

然后编译器报告:

[DCC Error] WeirdSets.dpr(9): E1012 Constant expression violates subrange bounds

这里的基本问题是集合限制为256个元素,因此只要有一个长度大于该数组的数组,代码就会停止工作。

遗憾的是,德尔福对集合的支持是不够的,迫切需要关注。


我也想知道你是否真的打算写

if I in [0..High(L)] then

如果是,那么我建议您使用InRange中的Math功能。

if InRange(I, 0, High(L)) then

甚至更好

if InRange(I, low(L), High(L)) then

答案 2 :(得分:2)

OP代码最严重的问题是in运算符的大小限制为set,即[0..255]。在任何32位版本的Delphi中试试这个以避免64位问题:

var
  I: Integer;
  L: array of Integer;

begin
  SetLength(L, 1000);
  I:= 999;
  Assert(I in [0, High(L)]);  // fails !
end;

如果Length(L) <= 256总是如此,OP很幸运,否则这是你可能从未想过的错误。

要查找此错误切换范围,请检查:

{$R+}
procedure TForm1.Button2Click(Sender: TObject);
var
  I: Integer;
  A: array of Integer;

begin
  SetLength(A, 1000);
  I:= 999;
  if I in [0, High(A)] then ShowMessage('OK!');  // Project .. raised exception
                          // class ERangeError with message 'Range check error'.
end;