【问题标题】:Issue with in operator in Delphi 64 bits projectDelphi 64 位项目中的 in 运算符问题
【发布时间】:2011-12-11 03:19:11
【问题描述】:

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

编译器引发此错误

E2010 不兼容的类型:'Integer' 和 '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)] 

【问题讨论】:

  • 你可以考虑更快的cardinal(I)<=cardinal(high(L)),它只用一个操作码进行比较,解决类型转换问题。
  • 你应该非常小心确保High(LArray) <= 255
  • @Arnaud 是什么让您认为性能如此重要?为什么您更喜欢使用具有隐藏细微差别和范围错误的神秘难读代码?这对我来说毫无意义。看起来像是过早优化的经典案例。
  • @salvador 如果您打算以这种方式使用集合,请确保您的代码包含high(L)>=low(Byte)high(L)<=high(Byte) 的断言,这样就可以了。

标签: delphi 64-bit delphi-xe2


【解决方案1】:

无法编译此代码,因为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]); 

这返回真,因为 257 的低字节是 1。

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

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

将返回 false 因为相当于

  Writeln( 1 in []); 

【讨论】:

  • 如果你使用高,你也应该使用低。硬编码 0 可能有效,但使用 Low 总是有效。
  • 根据文档集的基本类型必须不超过 256 个元素。那么 [0..integer(high(L))] 甚至可以工作吗?
  • @DavidHeffernan [0..integer(high(L))] 不等于 [0,integer(high(L))]
  • @Krom,InRange 函数测试 Value 是否介于最小值和最大值之间,因此在这种情况下不适用,因为if I in [0, Integer(High(L))] then 相当于写了if (i=0) or (i=Integer(High(L))) then
  • @RRUZ 查看我的最新更新。 in 运算符根本不适合这里的目的。
【解决方案2】:

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 个元素,因此一旦您的数组长度大于该长度,您的代码就会停止工作。

遗憾的是,Delphi 对集合的支持根本不够,急需关注。


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

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

如果是这样,那么我建议您使用 Math 中的 InRange 函数。

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

甚至更好

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

【讨论】:

  • 很高兴终于解决了这个问题。对于真正正确和深入的答案,您应该得到更多的支持)
  • +1 好答案大卫,我也更新了我的答案,提供了有关 In 运算符的更多信息。
【解决方案3】:

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;

【讨论】:

  • @LU RD - 没有错别字,Length(L) <= 256 ==> High(L) <= 255 如果你是认真的。
  • 抱歉,混淆了高度和长度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2021-08-21
  • 1970-01-01
  • 2022-08-10
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多