Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.06.26;
Скачать: CL | DM;

Вниз

Процедура FillChar и динамические массивы   Найти похожие ветки 

 
Baks   (2011-01-28 00:51) [0]

Какими последствиями грозит выполнение следующего кода:

type
 TMySLst = array of String;

 TMyRec = record
   MySLst: TMySLst;
   ID: Integer;
 end;

procedure Test;
var
 MyRec: TMyRec;
begin
 FillChar(MyRec, SizeOf(MyRec), 0);

 SetLength(MyRec.MySLst, 5);
end;

?


 
tesseract ©   (2011-01-28 01:02) [1]

AccessViolation  или перетирание указателей и плавающий баг. SizeOf(MyRec) вообще должен выдать 8 - но наверно этого хватит.


 
Германн ©   (2011-01-28 01:08) [2]


> AccessViolation

AV вряд ли будет, к сожалению. :(


 
tesseract ©   (2011-01-28 01:10) [3]


> AV вряд ли будет, к сожалению. :(


Сразу не будет. Будет потом в самых неожиданных местах.


 
Baks   (2011-01-28 01:33) [4]

Как я понимаю повреждение данных за пределами памяти выделенной под MyRec не должно произойти т.к. SizeOf вернёт 8, а FillChar затрёт указатель на MySLst (4) и обнулит ID (4).


 
tesseract ©   (2011-01-28 01:37) [5]


> а FillChar затрёт указатель на MySLst (4)


Скорее всего так и будет. Но тонкости возможны. При обращении к массиву получишь ты свой AV.


 
Германн ©   (2011-01-28 01:41) [6]


> FillChar затрёт указатель на MySLst (4) и обнулит ID (4).

Да.


 
tesseract ©   (2011-01-28 01:52) [7]

Да и как ты думаешь, что произойдет, когда будет dispose по дин массиву ?


 
Baks   (2011-01-28 01:57) [8]


> При обращении к массиву получишь ты свой AV


Интересно то, что при вызове SetLength(MyRec.MySLst, 5) никакого AV не происходит. А MyRec.MySLst содержит 5 пустых строк.


> Да и как ты думаешь, что произойдет, когда будет dispose
> по дин массиву ?


Не знаю.


 
Германн ©   (2011-01-28 02:15) [9]


> Интересно то, что при вызове SetLength(MyRec.MySLst, 5)
> никакого AV не происходит.

Интересно. Сделай тестовый проект и выложи его куда-нибудь. Пока сабж только "твои предположения".

P.S. AV как раз тем и страшен, что неуловим на этапе отладки! Можно туеву хучу лет отлаживать программу в отладчике и не иметь никаких проблем. А на туева хуча + 1 год получить AV! И как правило эта ошибка возникает уже после того, как ПО передано заказчику. :(


 
tesseract ©   (2011-01-28 02:28) [10]


> никакого AV не происходит. А MyRec.MySLst содержит 5 пустых
> строк.


Неее Delphi хранит размер указаетеля в отдельной области. Поэтому setlength только его размер увеличивает - прямого обращения по адресу не происходит. Если у тебя такие вопросы возникают - поиграйся с Си. Там быстро с указателями порядок наступает.


 
Sapersky   (2011-01-28 04:24) [11]

AV не будет, может быть утечка в том случае, если массив перед FillChar не пустой. Но поскольку в данном конкретном случае он пустой - всё OK.
nil/0 для дин.массивов/строк - это нормальное состояние, означает "нет данных". Вот если туда попадёт мусор (случайное значение <> 0) - тогда будет AV, поскольку компилятор посчитает этот мусор за предыдущее значение строки и попытается её освободить.
FreeMem/TObject.Free, кстати, тоже нормально отрабатывают на nil (ничего не делают).


 
oxffff ©   (2011-01-28 09:50) [12]


> Baks   (28.01.11 00:51)  
> Какими последствиями грозит выполнение следующего кода:
>
> type
>  TMySLst = array of String;
>
>  TMyRec = record
>    MySLst: TMySLst;
>    ID: Integer;
>  end;
>
> procedure Test;
> var
>  MyRec: TMyRec;
> begin
>  FillChar(MyRec, SizeOf(MyRec), 0);
>
>  SetLength(MyRec.MySLst, 5);
> end;
>
> ?


Усе будет нормуль. Если код имеено такой. Для записей с управляемыми полями ставляется вызов конструктора по умолчанию, который их инициализирует в 0.


 
oxffff ©   (2011-01-28 09:53) [13]


>  FillChar(MyRec, SizeOf(MyRec), 0);


Ето вообще зачем?


 
RWolf ©   (2011-01-28 10:32) [14]


>  Для записей с управляемыми полями ставляется вызов конструктора
> по умолчанию, который их инициализирует в 0.

то есть, MyRec можно и не обнулять, несмотря на то, что она на стеке?


 
RWolf ©   (2011-01-28 10:35) [15]

или конструктором обнуляются только управляемые поля?


 
han_malign   (2011-01-28 10:54) [16]


> или конструктором обнуляются только управляемые поля?

- угадал... См. System._Initialize/_Finalize...
(Если что - _Finalize делается в неявной try/finally секции при выходе стековой структуры с магическими полями из области видимости)
Эти функции также вызываются при типизированном new/dispose(но не при GetMem/FreeMem), и их можно вызывать явно(убрав магическое подчеркивание), если по каким то причинам используются другие методы выделения памяти(это удобнее нудного обнуления магических полей, и структура легко масштабируется)...


 
DiamondShark ©   (2011-01-28 11:37) [17]

Сколько профессионалов, однако


 
Baks   (2011-01-28 18:13) [18]

Этот код конечно неправильный, но если такая ошибка допушена, то как я
понял к большим проблемам (кроме утечки памяти) это не приведёт.
Правильно?


 
Leonid Troyanovsky ©   (2011-01-28 18:30) [19]


> Baks   (28.01.11 18:13) [18]

> понял к большим проблемам (кроме утечки памяти) это не приведёт.

Маловато будет?

--
Regards, LVT.


 
Sha ©   (2011-01-28 18:53) [20]

> Baks   (28.01.11 18:13) [18]

Интересно стало, а какие проблемы считаются большими?
Нечто большее, чем неработоспособность программы?
Это, наверно, когда она приносит вред, а не пользу?
Ну, такую программу еще надо постараться написать без ошибок.
А с таким отношением, скорее всего, не получится.
Полегчало :-)


 
Baks   (2011-01-30 12:33) [21]

Подскажите ещё, если сделать так:

type
TMySLst = array of String;

TMyRec = record
  MySLst: TMySLst;
  ID: Integer;
end;

function Test: TMyRec;
begin
 Result.
end;

Если запустить функцию дважды то при втором входе Result не будет пустым
изначально. Подскажите, как нужно очистить Result в данном случае?


 
jack128_   (2011-01-30 13:10) [22]

Finalize(Result);


 
Baks   (2011-01-30 13:34) [23]

А если очистить так:

SetLength(Result.MySLst, 0);
ID := 0;

Так, будет правильно? Не будет утечки памяти?


 
Baks   (2011-01-30 21:25) [24]

Обнаружил, что вызов данной функции в цикле приводит к утечке памяти. Почему?

 TMyRec = record
   MStr: String;
   UStr: String;
   ID: Integer;
 end;

function GetER: TMyRec;
begin
 ZeroMemory(@Result, SizeOf(Result));

 Result.MStr := "Text";
 Result.UStr := "Text";
 Result.ID := 1;
end;

При втором и последующих входах Result содержит прежние данные.
Чтобы их очистить я выполняю ZeroMemory(@Result, SizeOf(Result));
Но обнаружил, что если эту процедуру убрать, то утечки памяти не будет.
Почему? И как правильно в данном случае очищать Result?


 
oxffff ©   (2011-01-30 21:42) [25]


> Baks   (30.01.11 21:25) [24]
> Обнаружил, что вызов данной функции в цикле приводит к утечке
> памяти. Почему?


Ввиду оптимизации, чтобы избежать лишних копирований из записи активации метода в приемник результата, результат представляется неявным var параметром. Для этих целей при вызове компилятор подставляет временную переменную, которая переиспользуется в последующих вызовах. Поэтому при последующих вызовах, результат на входе может быть уже не пустым. Утечка происходит потому что используемая очистка "грубая", минуя семантику управляемых типов в данном случае динамических массивов.


 
Baks   (2011-01-30 21:50) [26]


> минуя семантику управляемых типов в данном случае динамических
> массивов


Так, здесь же нет дин. массива.

TMyRec = record
  MStr: String;
  UStr: String;
  ID: Integer;
end;

Или имеется ввиду String?


 
Baks   (2011-01-30 21:59) [27]

Всё, кажется разобрался. Если сделать например String[250], то память остаётся неизменной. Я забыл, что строки это тоже дин. массивы.


 
Sha ©   (2011-01-30 22:16) [28]

Кошмар. Сплошное латание дыр.
1. Откуда берется утечка, а точнее ошибка в программе из-за которой ты теряешь данные, ты так и не понял.
2. Не разобрался с возвратом записи в качестве результата, а это запросто может стать в твоем случае еще одними граблями.


 
Baks   (2011-01-30 22:25) [29]

Почему не разобрался?
При выполнении ZeroMemory не освобождается память выделенная для содержимого строк.


> Не разобрался с возвратом записи в качестве результата


Что имеется ввиду? Хотелось бы подробней.


 
Sha ©   (2011-01-30 22:35) [30]

> При выполнении ZeroMemory не освобождается память выделенная для содержимого строк.

Она также не освобождается при выполнении оператора i:=i+1;
Освободить-то как?

>> Не разобрался с возвратом записи в качестве результата
> Что имеется ввиду? Хотелось бы подробней.

[25] изучи.
Твой ZeroMemory затирает "прежние данные", но вовсе не там, где ты думаешь.


 
Baks   (2011-01-30 22:40) [31]

Ну так как же тогда освобождать эти данные на входе в функцию?


 
Baks   (2011-01-30 22:41) [32]

Конечно ZeroMemory здесь не подходит. Это понятно.


 
Sha ©   (2011-01-30 22:44) [33]

Тут не в нем дело даже, а в том, что утечка может произойти из-за копирования записи.
Короче не используй ты функцию, пока не понял, что к чему.


 
Sha ©   (2011-01-30 23:14) [34]

Опечатался:
> что утечка может произойти
> что ошибка может произойти

При копировании утечки, очевидно, не будет.


 
Sha ©   (2011-01-30 23:23) [35]

В общем вот пример, чтобы было ясно, о чем речь:

type
 TRec = record
   s: string;
   end;

function GetRec: TRec;
begin;
 Result.s:=Result.s + "a";
 end;

var
 r: TRec;

procedure TForm1.Button1Click(Sender: TObject);
var
 i: integer;
begin;
 for i:=1 to 3 do begin;
   r.s:=r.s + "a";
   r.s:=r.s + "b";
   end;
 Caption:=r.s;
 end;

procedure TForm1.Button2Click(Sender: TObject);
var
 i: integer;
begin;
 for i:=1 to 3 do begin;
   r:=GetRec;
   r.s:=r.s + "b";
   end;
 Caption:=r.s;
 end;


 
Baks   (2011-01-31 00:00) [36]

Ну, так при входе в GetRec нужно обязательно Result очищать.
А так при повторном входе, мы фактически используем остатки от прошлого выполнения. Так?


 
Baks   (2011-01-31 00:07) [37]

В данном случае надо сделать так:

procedure GetRec(var R: TRec);
begin;
 R.s:=R.s + "a";
end;

procedure TForm1.Button2Click(Sender: TObject);
var
i: integer;
begin;
for i:=1 to 3 do begin;
  GetRec(r);
  r.s:=r.s + "b";
  end;
Caption:=r.s;
end;


 
Sha ©   (2011-01-31 00:09) [38]

> Baks   (31.01.11 00:00) [36]

Ты пример [35] запускал?
Если да, то должен понять, что никакая очистка Result в GetRec
не позволит получить в Button2Click тот же Caption, что и в Button1Click.


 
Sha ©   (2011-01-31 00:11) [39]

> Baks   (31.01.11 00:07) [37]

Ага.
Осталось научиться не терять данные.


 
Baks   (2011-01-31 00:22) [40]

Запускал. Естественно, очистка не поможет получить такой тезультат, как в Button1 таким способом:

function GetRec: TRec;
begin;
 Result.s:=Result.s + "a";
end;

это нельзя делать. К чему мы прибавляем "а"? К каким-то остаткам.



Страницы: 1 2 вся ветка

Текущий архив: 2011.06.26;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.011 c
3-1260904454
lamer6666
2009-12-15 22:14
2011.06.26
Ошибка выполнения ZQuery.Post


2-1300378553
Алексей Гость
2011-03-17 19:15
2011.06.26
Не могу разобраться с DLL


15-1300117961
Сергей К.
2011-03-14 18:52
2011.06.26
Bob Marley - no woman, no cry


15-1299756594
Юрий
2011-03-10 14:29
2011.06.26
С днем рождения ! 8 марта 2011 вторник


15-1299760193
Юрий
2011-03-10 15:29
2011.06.26
С днем рождения ! 9 марта 2011 среда