Проблема с оператором in в проекте Delphi 64 bit - PullRequest
6 голосов
/ 11 декабря 2011

Я портирую проект 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)] 

Ответы [ 3 ]

7 голосов
/ 11 декабря 2011

Этот код не может быть скомпилирован, потому что функция 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

UPDATE

Проверьте дополнительную информацию, предоставленную Дэвидом, и помните, что нужно быть очень осторожным при использовании оператора 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 []); 
4 голосов
/ 11 декабря 2011

То, что говорит 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

Если это так, я бы порекомендовал вам использовать функцию InRange из Math.

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

или даже лучше

if InRange(I, low(L), High(L)) then
2 голосов
/ 11 декабря 2011

Наиболее серьезная проблема с кодом 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 всегда, в противном случае это ошибка, о которой вы, вероятно, никогда не задумывались.

Чтобы найти эту ошибку, проверьте диапазон:

{$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;
...