我正在将一个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)]
答案 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;