64-битная арифметика указателя PChar выдает ошибку проверки диапазона

Я отслеживаю ошибку глубоко в стороннем коде, где оператор

 fStringLen := Str - fToIdent;

выдает ошибку во время выполнения:

  • Str — это частный параметр функции PChar, на данный момент это ''
    (Изначально не пусто, но Inc(Str) выполняется до конца цикла)
  • fStringlen — целочисленное частное свойство, значение 0.
  • fToIdent — это частная собственность PChar, она показывает nil {%0} в окне инспектора:

На данный момент неясно, было ли что-нибудь. Отслеживая все происшествия, похоже, с fToIndent Свойство PChar (у него нет установщика, на который я мог бы поставить точку останова), или if и это всего лишь начальное состояние после создания его объекта.

Существует несколько более старая 32-разрядная версия нашего программного обеспечения и более старая версия стороннего программного обеспечения, но этот фрагмент кода идентичен. Значения те же, если я запускаю код, но тогда ошибки проверки диапазона нет.

Кстати, это код, см. последний оператор:

    function TdaSynHighlighterSQL.HashKey(Str: PChar): Integer;
    // called with Str = 'absolute'

      function GetOrd: Integer;
      begin
        case Str^ of
          '_': Result := 1;
          'a'..'z': Result := 2 + Ord(Str^) - Ord('a');
          'A'..'Z': Result := 2 + Ord(Str^) - Ord('A');
          '@':
            if fDialect in [sqlMSSQL7, sqlMSSQL2K] then
              Result := 24
            else
              Result := 0;
          else Result := 0;
        end;
      end;

    begin
      Result := 0;
      while IsIdentChar(Str^) do  // Always true until Str=''
      begin
        Result := (2 * Result + GetOrd) and $FFFFFF;
        inc(Str);
      end;
      Result := Result and $FF; // 255
      fStringLen := Str - fToIdent;
    end;

Как лучше это исправить?
Мне кажется странным, что этот код все еще не будет 64-битным доказательством.

если fToIdent равно нулю, а Str указывает на конец данной строки и эта строка выделена в верхней позиции памяти, то это может вызвать эту ошибку диапазона. ИМХО следует fToIdent указать на начало строки для расчета длины.

Uwe Raabe 10.05.2024 14:12

Спросите себя, как можно поместить 64-битное значение в 32-битную переменную, и вы увидите проблему.

David Heffernan 11.05.2024 00:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
170
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема вызвана использованием цикла while. Во время выполнения вашего fStringLen := Str - fToIdent; он уже указывает на память, превышающую длину вашей строки, что затем вызывает ошибку проверки диапазона.

Рассмотрим этот простой пример цикла while, где в качестве управляющего значения используется простое целое число.

while I < 10 do
begin
  Memo1.Lines.Add('Value of I in current loop cycle is ' +IntToStr(I));
  Inc(I,1);
end;
Memo1.Lines.Add('Value of I after loop is: '+IntToStr(I));

Вы могли бы ожидать, что значение управляющей переменной I будет равно 9 после завершения цикла while, но это не так. На самом деле это 10. Почему так.

Это связано с тем, что весь цикл проверяет значение управляющей переменной перед началом каждого цикла. Но я увеличил значение управляющей переменной за пределы желаемого диапазона в последнем цикле цикла.

Поэтому вам может потребоваться вычислить fStringLen внутри цикла, прежде чем увеличивать значение Str.

while IsIdentChar(Str^) do  // Always true until Str=''
begin
  Result := (2 * Result + GetOrd) and $FFFFFF;
  fStringLen := Str - fToIdent;
  inc(Str);
end;

Однако это приведет к обновлению fStringLen для каждого символа в вашей строке.

Но если вы хотите избежать этого и по-прежнему вычислять fStringLen после цикла, вам нужно уменьшить значение Str на единицу раньше, чтобы оно отображало позицию последнего символа в вашей строке.

PS: Я не понимаю, почему ваш код вызывает исключение диапазона только в 64-й сборке. Это также должно было вызвать исключение диапазона в 32-битной сборке.

Перемещение вычисления fStringLen перемещает только место ошибки ;-)

Jan Doggen 16.05.2024 11:46
Ответ принят как подходящий

Комментарий Дэвида Хеффернана указал в правильном направлении:

fToIdent не инициализируется, поскольку функция IdentKind(), которая его устанавливает, еще не была вызвана (это не ошибка).

В 32-битной версии fStringLen становится 245556086, если fToIdent=nil и оно соответствует целому числу. В 64-битной версии fStringLen становится 1513182692840, если fToIdent=nil и оно не помещается в целое число.

Мы сделали быстрый патч в исходниках:

// fStringLen := Str - fToIdent;
// Prevent rangecheck error
if (fToIdent=nil) then
  fStringLen := 0
else
  fStringLen := Str - fToIdent;
 

КСТАТИ. Это было в ReportBuilder 22.03, на форуме поддержки получил такой ответ:

Версия SynEdit, используемая в ReportBuilder, была сильно адаптирована и изначально не предназначалась для 64-битных приложений. Мы преобразовали его для этого, однако иногда возникают проблемы, которые ускользают от внимания. Теперь для RB 22.04 доступен патч, устраняющий эту проблему.

Мы еще не обновились до 22.04, поэтому я не знаю, как они это исправили.

Другие вопросы по теме