23 марта 2018 г.

Задачка №23

Есть ли ошибка в этом коде?

Код предположительно обрабатывает файл, открывает его, читает данные, что-то с ними делает, затем сохраняет результат в другой файл.
var
  Stream: TFileStream;
begin
  Stream := nil;
  try
    // что-то делаем
    Stream := TFileStream.Create({ ... });
    // что-то делаем
  finally
    Stream.Free;
  end;
  try
    try
      Stream := TFileStream.Create({ ... });
    except
      Exit;
    end;
    // что-то делаем
  finally
    Stream.Free;
  end;
end;

Ответ.

11 комментариев:

  1. Может произойти повторный Free на уже освобожденной переменной.
    Следует использовать FreeAndNil(Stream);

    ОтветитьУдалить
  2. ну какая то совсем простая задачка :)
    Конечно AV будет.
    в первом
    Stream.Free;
    Экземпляр класса освободился, а ссылка (переменная Stream) не обнулена
    Далее в TFileStream.Create происходит exception,
    except его гасит. Далее Exit и бодро идем... в последний finally
    где Stream.Free; и AV :)

    ОтветитьУдалить
  3. Создание экземпляров стримов применять ДО try верхней вложенности

    ОтветитьУдалить
  4. Да, видимо, если в первом "что-то делаем" что-то пойдет не так, то Create не выполнится, а перескочет сразу на Free.

    ОтветитьУдалить
  5. 1) Если в конструкторе произойдет исключение, то неявно вызовется деструктор.
    finally выполнится всегда, а там уже битый указатель Stream
    2) После Exit также произойдут вызовы всех finally, а там тот-же битый Stream

    ОтветитьУдалить
    Ответы
    1. "После Exit также произойдут вызовы всех finally"
      А вот этого я не знал :o

      Удалить
  6. Первый блок просто уродливый, но работоспособный.
    2 Sibedir: первый "что-то делаем" поднимет исключение, управление перейдет к процедуре Free, которая защищена от таких ситуаций и не станет в этом случае звать деструктор.

    Второй блок тоже уродливый, но не всегда работоспособен, а именно - если в первом блоке не было вовсе исключений, а во втором они случились в строке конструкции, то финализация во втором блоке вызовет второй раз деструктор одного и того же экземпляра, в котором будет обращение к полю [EAX]TFileStream.FHandle, что может привести к AV.

    ОтветитьУдалить
    Ответы
    1. Да, спасибо. Действительно я не учел, что там nil, а [nil].Free не выполнится.

      Удалить
    2. "[nil].Free" выполнится.
      Всмысле рейзить не будет.
      Для того Free и создан вместо Destroy.

      Удалить
  7. Я так понял, что нужно повнимательнее разобраться со стеком вызовов при обработке исключений?

    ОтветитьУдалить
  8. Вроде, конкретных ошибок нет. Но create'ы внутри try..finally - потенциально опасно и криво.

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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

Примечание. Отправлять комментарии могут только участники этого блога.