Инициализировать результат строковой функции?

Я просто отлаживал проблему с функцией, которая возвращает строку, которая имеет меня взволнованный. Я всегда предполагал, что неявная переменная Результата для функций, которые возвращают строку, была бы пуста в начале вызова функции, но следующий (упрощенный) код привел к неожиданному результату:

function TMyObject.GenerateInfo: string;

        procedure AppendInfo(const AppendStr: string);
        begin
          if(Result > '') then
            Result := Result + #13;
          Result := Result + AppendStr;
        end;

begin
  if(ACondition) then
    AppendInfo('Some Text');
end;

Вызывание этой функции многократно привело к:

"Some Text"

в первый раз,

"Some Text"
"Some Text"

во второй раз,

"Some Text"
"Some Text"
"Some Text"

в третий раз, и т.д.

Для фиксации его, я должен был инициализировать Результат:

begin
  Result := '';
  if(ACondition) then
    AppendInfo('Some Text');
end;

Действительно ли необходимо инициализировать результат строковой функции? Почему (технически)? То, почему компилятор не испускает предупреждение "Возвращаемое значение W1035 функции 'xxx', могло бы быть не определено" для строковых функций? Я должен пройти весь свой код, чтобы удостовериться, что значение установлено, поскольку это не надежно для ожидания пустой строки из функции, если результат явно не установлен?

Я протестировал это в новом тестовом приложении, и результатом является то же.

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  S: string;
begin
  for i := 1 to 5 do
    S := GenerateInfo;
  ShowMessage(S); // 5 lines!
end;
29
задан mjn 15 July 2010 в 10:40
поделиться

6 ответов

Это не ошибка, а « функция »:

Для строки, динамический массив, метод указатель или вариант результата, эффекты такие же, как , если результат функции был объявлен как дополнительный параметр var после заявленные параметры. Другими словами, вызывающий передает дополнительный 32-битный указатель, указывающий на переменную в который должен вернуть результат функции.

Т.е. your

function TMyObject.GenerateInfo: string;

Действительно ли это:

procedure TMyObject.GenerateInfo(var Result: string);

Обратите внимание на префикс « var » (а не « out », как вы могли ожидать!).

Это ТАКОЕ не интуитивно понятно, поэтому приводит ко всевозможным проблемам в коде. Рассматриваемый код - всего лишь один пример результатов этой функции.

Просмотрите и проголосуйте за этот запрос .

33
ответ дан 28 November 2019 в 01:51
поделиться

Мы сталкивались с этим раньше, я думаю, может быть, еще в Delphi 6 или 7. Да, даже если компилятор не беспокоится о предупреждении вас, вам все же нужно инициализируйте свои строковые переменные Result именно по той причине, с которой вы столкнулись. Строковая переменная инициализируется - она ​​не начинается как ссылка на мусор - но, похоже, не инициализируется re , когда вы этого ожидаете.

Что касается того, почему это происходит ... не уверен. Это ошибка, поэтому не обязательно указывать причину. Мы видели это только тогда, когда вызывали функцию несколько раз в цикле; если мы вызывали его вне цикла, он работал должным образом.Похоже, что вызывающий выделял место для переменной Result (и повторно использовал ее, когда он неоднократно вызывал одну и ту же функцию, что приводило к ошибке), а не функция , выделяющая свою собственную строку (и выделяя новый для каждого звонка).

Если вы использовали короткие строки, то вызывающий объект выделяет буфер - это давно устоявшееся поведение для типов с большими значениями. Но для AnsiString это не имеет смысла. Возможно, команда компиляторов просто забыла изменить семантику, когда они впервые реализовали длинные строки в Delphi 2.

4
ответ дан 28 November 2019 в 01:51
поделиться

Это не ошибка. По определению никакая переменная внутри функции не инициализируется, включая Результат.

Таким образом, ваш Результат - undefind при первом вызове, и может содержать что угодно. Как это реализовано в компиляторе, не имеет значения, и вы можете получить разные результаты в разных компиляторах.

2
ответ дан 28 November 2019 в 01:51
поделиться

Похоже, что ваша функция должна быть упрощена следующим образом:

function TMyObject.GenerateInfo: string;
begin
  if(ACondition) then
    Result := 'Some Text'
  else
    Result := '';
end;

Обычно вы не хотите использовать Result справа от присваивания в функции.

В любом случае, строго в иллюстративных целях, вы также можете сделать это, хотя и не рекомендуется:

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  S: string;
begin
  for i := 1 to 5 do
  begin
    S := ''; // Clear before you call
    S := GenerateInfo;
  end;
  ShowMessage(S); // 5 lines!
end;
1
ответ дан 28 November 2019 в 01:51
поделиться

Это похоже на ошибку в D2007. Я только что протестировал это в Delphi 2010 и получил ожидаемое поведение. (1 строка вместо 5.)

0
ответ дан 28 November 2019 в 01:51
поделиться

Если вы думаете, что автоматическое управление строками сделано для облегчения вашей жизни, вы правы лишь отчасти. Все это также сделано для того, чтобы логика строк была последовательной и не возникало побочных эффектов.

Во многих местах есть строки, переданные по ссылке, переданные по значению, но все эти строки ожидают VALID строк, в счетчике управления памятью которых есть допустимое значение, а не значение мусора. Поэтому для того, чтобы строки оставались действительными, единственное, что можно сказать наверняка, - это то, что они должны быть инициализированы при первом введении. Например, для любой строки локальной переменной это необходимо, поскольку именно здесь вводится строка. Любое другое использование строк, включая function (): string (которая фактически является процедурой (var Result: string), как правильно указал Александр), ожидает только действительных строк в стеке, а не инициализированных . И действительность здесь исходит из того факта, что конструкция (var Result: string) говорит, что «я жду действительной переменной, которая определенно была введена ранее».ОБНОВЛЕНИЕ: из-за этого фактическое содержимое Result является неожиданным, но из-за той же логики, если это единственный вызов этой функции, который имеет локальную переменную слева, пустота строки в этом случае гарантируется.

0
ответ дан 28 November 2019 в 01:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: