Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Баг компилятора или (пример)? Неверно работает стек? 
:(
    Опции темы
sptim
Дата 24.3.2016, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 11.8.2009

Репутация: нет
Всего: нет



Добрый день!
Обращаюсь к знатокам, сам пишу на Делфи только в рамках сопровождения старых проектов и тут намедни поймал очень удививший меня баг. Ниже я специально набросал микро-тест воспроизводящий эту ситуацию:
Код

unit Test1;

interface

type
  PTest1 = ^TTest1;
  TTest1 = class
    fpos: integer;

    constructor Create();
    function pos(n: integer): PTest1;
    function putAt(n: integer; v: int64): PTest1;
    function put(v: int64): PTest1;
  end;

implementation

var
  t: TTest1;

constructor TTest1.Create();
begin
  fpos := 0;
end;

function TTest1.pos(n: integer): PTest1;
begin
  fpos := n;
  Result := @self;
end;

function TTest1.putAt(n: integer; v: int64): PTest1;
begin
  pos(n);
  Result := @self;
end;

function TTest1.put(v: int64): PTest1;
begin
  Result := putAt(fpos, v); // Result верный.
  pos(fpos+4); // После вызова этой ф-ции Result переопределяется "левым" значением (!!!)
end;

begin
  t := TTest1.Create();
  t.put(5).put(5); 
  // Первый вызов put возвращает "левый" указатель(!), соответственно
  // выбрасывается исключение при попытке вызова второго put
end.

Параметры среды: Delphi7, оптимизации отключены, WinXP sp2 32bit.
Причем, если заменить Int64 на Integer - ошибок не будет (!). Т.е. это какой-то косяк компилятора (работа со стеком при Int64 аргументах)? Пробовал в инете поискать - ничего не нагуглилось.
Может я чего-то недопонимаю - тогда буду благодарен за объяснение. 
PM MAIL   Вверх
dnek
Дата 25.3.2016, 10:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 0
Регистрация: 1.4.2013

Репутация: нет
Всего: нет



Не знаю поможет или нет, но правильнее было бы написать
Код

t.put(5)^.put(5);


Этот ответ добавлен с нового Винграда - http://vingrad.com
PM MAIL   Вверх
kami
Дата 25.3.2016, 18:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Переменная, содержащая экземпляр класса - это уже указатель.
Поэтому объявление 
Цитата(sptim @  24.3.2016,  21:15 Найти цитируемый пост)
PTest1 = ^TTest1;

абсолютно излишне. Таким образом вы декларируете "указатель на указатель".
Сам Result в вашем коде содержит валидный указатель на область памяти, в которой должен содержаться указатель на экземпляр класса. Вот только компилятор не знает о том, что участок памяти, на который ссылается ваш указатель, нельзя модифицировать.  Чтобы сказать ему об этом - необходимо явно зарезервировать область памяти, т.е. сделать
Код

New(Result);

В противном случае возможны вот такие неочевидные казусы. При этом нельзя забывать правило: если что-то выделил/создал - не забудь его удалить! (антипод New - процедура Dispose).

Повторюсь - в вашем случае объявление PTest1 излишне. Так стоит поступать при динамическом создании record-ов, но никак не экземпляров классов.
Преобразуйте свой код так, чтобы он возвращал себя же (компилятор нормально переваривает указание типа класса внутри самого класса):
Код

function TTest1.pos(n: integer): TTest1;
begin
  fpos := n;
  Result := Self;
end;

И не забудьте про высвобождение ресурсов:
Код

begin
  t := TTest1.Create();
  try
    t.put(5).put(5); 
  finally
    t.Free;
  end;
end.

PM MAIL WWW   Вверх
dnek
Дата 26.3.2016, 11:51 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 0
Регистрация: 1.4.2013

Репутация: нет
Всего: нет



Проблема здесь заключается не в PTest1 и не в Result (с ним как раз все в порядке и выделять память под него не надо). В данном примере проблема в строке
Код

  Result := @self;

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

Этот ответ добавлен с нового Винграда - http://vingrad.com
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0796 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.