Подтвердить что ты не робот

Результат инициализации строки?

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

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 "может быть undefined" для строковых функций? Нужно ли мне переходить ко всему моему коду, чтобы убедиться, что значение установлено, так как ненадежно ожидать пустую строку от функции, если результат явно не установлен?

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

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  S: string;
begin
  for i := 1 to 5 do
    S := GenerateInfo;
  ShowMessage(S); // 5 lines!
end;
4b9b3361

Ответ 1

Это не ошибка, но " feature":

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

т.е. ваш

function TMyObject.GenerateInfo: string;

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

procedure TMyObject.GenerateInfo(var Result: string);

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

Это SUCH неинтуитивно, поэтому это приводит ко всем типам проблем в коде. Код, о котором идет речь, - один из примеров результатов этой функции.

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

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;

Ответ 5

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

Ответ 6

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

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

Ответ 7

Ответ Алекс почти всегда прав, и он отвечает, почему я видел странное поведение, которым я был, но это не вся история.

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

По-видимому, для фактического модуля программы существует другое правило.

По общему признанию, это угловой случай.

program Project1;

{$APPTYPE CONSOLE}

uses System.SysUtils;

  function PointlessFunction: string;
  begin
  end;

  procedure PointlessProcedure(var AString: string);
  begin
  end;

var
  sTemp: string;
begin
  sTemp := '1234';
  sTemp := PointlessFunction;
  //PointlessProcedure(sTemp);
  WriteLn('Result:' + sTemp);
  ReadLn;
end.