Форум: "Прочее";
Текущий архив: 2011.06.26;
Скачать: [xml.tar.bz2];
ВнизПроцедура 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;
это нельзя делать. К чему мы прибавляем "а"? К каким-то остаткам.
← →
Кто б сомневался © (2011-01-31 01:32) [41]Я кстати вместо FillChar, юзаю апишную ZeroMemory - т.к. в FillChar все равно ее вызывает потом.
← →
Германн © (2011-01-31 01:53) [42]
> Кто б сомневался © (31.01.11 01:32) [41]
>
> Я кстати вместо FillChar, юзаю апишную ZeroMemory - т.к.
> в FillChar все равно ее вызывает потом.
Очень неудачный топик для подобного заявления. Примите и прочь! :)
← →
Baks (2011-01-31 03:04) [43]
> Очень неудачный топик для подобного заявления
Да, лучше не напоминать, я их (FillChar, ZeroMemory) теперь обеих боюсь.
> а это запросто может стать в твоем случае еще одними граблями
Похоже я наступил на детские грабли, а это ещё неприятнее :)
← →
Sha © (2011-01-31 08:01) [44]> Baks (31.01.11 00:22) [40]
> function GetRec: TRec;
> begin;
> Result.s:=Result.s + "a";
> end;
> это нельзя делать. К чему мы прибавляем "а"? К каким-то остаткам.Baks
К временной записи, которую завел компилятор.
А если нельзя, то почему же ты реботаешь с временной записью в [24] ?
> Baks (31.01.11 03:04) [43]
> Да, лучше не напоминать, я их (FillChar, ZeroMemory) теперь обеих боюсь.
Чего их бояться? Это все равно, что бояться использовать для строк операторinteger(s):=0;
который просто обнулит указатель на строку, а сама строка останется лежать в ОП. Он не для этого. Присвоить строке пустое значение можно и другими способами.
← →
Baks (2011-02-01 13:54) [45]
> почему же ты реботаешь с временной записью в [24] ?
Я её для этого очищаю и из неё ничего не читаю. И к прежнему содержимому не прибавляю.
← →
Sha © (2011-02-02 10:10) [46]> Baks (01.02.11 13:54) [45]
> Я её для этого очищаю
В итоге подсчет ссылок для строк, которые ты сформируешь во временной записи, работает неверно. Утечка.
Лучше не делать этого.
← →
имя (2011-03-12 18:49) [47]Удалено модератором
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2011.06.26;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.004 c