Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];
ВнизДинамический массив записей со строками. подскажите Найти похожие ветки
← →
SergP © (2013-11-06 18:33) [0]а то запутался...
type
TBaseRec = packed record
Cat:string[4];
relevant:byte;
question:string;
answer:string;
end;
...
var
DataBase: packed array of TBaseRec;
Если я меняю размер массива Setlength то строки в записях автоматически создаются и уничтожаются?
Если мне нужно раздвинуть или сдвинуть массив (вставка или удаление), то с помощью move такое уже нельзя делать?
← →
jumping jack (2013-11-06 18:40) [1]
type // заменил типы ДЛЯ ЯСНОСТИ ПОНИМАНИЯ, как они выглядят для компилятора
TBaseRec = packed record
Cat:bytes[5]; // эквивалентно string[4];
relevant:byte;
question:pointer; // эквивалентно string
answer:pointer;
end;
← →
Павиа (2013-11-06 19:35) [2]
> string[4]
Это строка фиксированной длины и она хранится в записе.
> answer:string;
Это длинная строка и она храниться в куче, а в записе храниться указатель.
> Если я меняю размер массива Setlength то строки в записях
> автоматически создаются и уничтожаются?
Строка создаётся и уничтожается в куче. А в записи меняется указатель.
> Если мне нужно раздвинуть или сдвинуть массив (вставка или
> удаление), то с помощью move такое уже нельзя делать?
Можно. Насколько помню даже задокументированно.
← →
НЛО (2013-11-06 20:07) [3]Двунаправленный список.
type
TBaseRec = packed record
Cat:string[4];
relevant:byte;
question:string;
answer:string;
prev: PBaseRec;
next: PBaseRec;
end;
...
var
DataBase: TBaseRec;
← →
НЛО (2013-11-06 20:08) [4]Вот тут обсуждалось с кодом.
http://www.cyberforum.ru/delphi-beginners/thread346181.html
← →
Rouse_ © (2013-11-06 22:23) [5]Move массива рекордов со строками, я бы не решился...
> Павиа (06.11.13 19:35) [2]
> Это длинная строка и она храниться в куче, а в записе храниться
> указатель.
Delphi не работает с кучей на уровне менеджера памяти.
← →
Rouse_ © (2013-11-06 22:26) [6]
> Rouse_ © (06.11.13 22:23) [5]
> Move массива рекордов со строками, я бы не решился...
ЗЫ: я не говорю что это не правильно, но что-то мне подсказывает что может быть заковыка (пока что сам не пойму где :)
← →
DVM © (2013-11-06 22:37) [7]
> но что-то мне подсказывает что может быть заковыка
с подсчетом ссылок вероятно
← →
DevilDevil © (2013-11-06 22:59) [8]> Если я меняю размер массива Setlength то строки в записях
> автоматически создаются и уничтожаются?
инициализируются и финализируются
> Если мне нужно раздвинуть или сдвинуть массив (вставка или
> удаление), то с помощью move такое уже нельзя делать?
Можно. Но при этом нужно самому корректно проводить операции инициализации и финализации
← →
RWolf © (2013-11-06 23:48) [9]а проще задействовать какой-нибудь TObjectList и не ломать голову почём зря.
← →
Sha © (2013-11-06 23:54) [10]> SergP © (06.11.13 18:33)
Посмотри реализацию TStringList. Станет понятно, как надо делать.
← →
SergP © (2013-11-07 01:10) [11]
>
> Можно. Но при этом нужно самому корректно проводить операции
> инициализации и финализации
А если переставить удаляемую запись в конец, чтобы потом не мучаться с ручной финализацией, т.е. чтобы строки финализировались при умньшении размера массива автоматически:var
x:TBaseRec;
Arlen:integer;
...
Arlen:=length(database)-1;
x:=database[i]; // сохраняем удаляемую запись.
move(database[i+1],database[i], (Arlen-i)*sizeof(x)); // подтягиваем хвост
database[Arlen]:=x; // удаляемую запись ставим в конец
setlength(database,Arlen); // уменьшаем длину массива на единицу
Ну и аналогично с раздвиганием, после увеличения длины массива последнюю запись сохранить и после собственно самого раздвигания, поставить ее на освободившееся место..
Так можно делать? Или тоже какие-то проблемы могут быть?
← →
Очень Злой (2013-11-07 01:23) [12]
> Посмотри реализацию TStringList. Станет понятно, как надо
> делать.
Завтра гляну, а то Дельфи сейчас под руками нет.
← →
DevilDevil © (2013-11-07 01:46) [13]> SergP © (07.11.13 01:10) [11]
Для начала ты должен понять, что Delphi внутри производит инициализации и финализации. Под инициализации и финализации подпадают следующие сложные типы:
- строки (string, AnsiString, WideString, UnicodeString). string[n] сложным не является
- динамические массивы (любые)
- Variant, OleVariant
- интерфейсы (interface, IInterface и прочее)
- структуры (record, object), содержащие внутри себя сложные типы
- статические массивы, содержащие сложные типы
Когда ты объявляешь переменные сложного типа и оперируешь ими - Delphi автоматически производит инициализации, финализации, подсчёты ссылок и прочее. К примеру если ты увеличиваешь размер динамического массива - то новые элементы инициализируются. А если ты уменьшаешь размер динамического массива - то сначала финализируются записи в конце и только потом размер массива уменьшается
На низком уровне строки(кроме string[n]), динамические массивы и интерфейсы - это указатели, занимающие 4 байт на x86 и 8 байт на x64. Variant - это структура 16 байт.
Для инициализации сложных типов - их достаточно обнулить. Для простоты вообще можно вызывать ZeroMemory или FillChar, если дело касается большой структуры или массива.
А вот финализацию из обычного кода не вызвать, по крайней мере в старых версиях Delphi. Нужно особым образом вызывать системную функцию. Для x86 такой вызов выглядит так, для x64 не экспериментировал:procedure Finalize(const Item: pointer; const tpinfo: ptypeinfo; const count: integer=1);
asm
jmp System.@FinalizeArray
end;
Теперь от теории к практике.
TBaseRec - это сложный тип, потому что содержит внутри себя сложные типы (2 строки). Значит для них производятся инициализации и финализации
Покажу кодом. Примерноtype
TBaseRecDynArray = array of TBaseRec;
procedure BaseRecDelete(var Arr: TBaseRecDynArray; const Index: integer);
var
Len: integer;
begin
// в наших манипуляциях пострадает текущий элемент с номером Index
// поэтому для начала его финализируем
Finalize(@Arr[Index], typeinfo(TBaseRec));
// теперь перетащим все элементы с Index+1 в Index
Len := Length(Arr);
CopyMemory(@Arr[Index], @Arr[Index+1], (Len-Index-1)*sizeof(TBaseRec));
// если мы просто изменим длинну массива, то по идее будет внутренняя ошибка
// потому что Delphi будет финализировать последнюю запись
// а там по идее уже хранится мусор. поэтому обнуляем поле, чтобы не было ошибок
ZeroMemory(@Arr[Len-1], sizeof(TBaseRec));
// теперь со спокойной совестью уменьшаем размер массива
// последний элемент будет финализироваться, но он считается пустым, поэтому всё okey
SetLength(Arr, Len-1);
end;
Точно так же можно проводит и другие манипуляции со сложными типами. Например вставлять в середину. Главное - понимать, как это работает на низком уровне
← →
DevilDevil © (2013-11-07 01:50) [14]> SergP © (07.11.13 01:10) [11]
А вообще да, можно и по простому
Для организации списков есть подходы
А если массив неупорядоченный, можно да, тупо писать из конца. Это кстати достаточно быстрый способtype
TBaseRecDynArray = array of TBaseRec;
procedure BaseRecDelete(var Arr: TBaseRecDynArray; const Index: integer);
var
Len: integer;
begin
Len := Length(Arr);
Arr[Index] := Arr[Len-1];
SetLength(Arr, Len-1);
end;
← →
DevilDevil © (2013-11-07 01:54) [15]* финализацию можно проводить и вручную
например вместоFinalize(@Arr[Index], typeinfo(TBaseRec));
можно написатьArr[Index].question := "";
Arr[Index].answer := "";
← →
Sapersky (2013-11-07 04:46) [16]Для финализации конкретного типа есть стандартная Finalize, которая работает как compiler magic, сама подставляет TypeInfo:
Finalize(Arr[Index]);
Автору вопроса: в новых версиях Дельфи можно использовать TList<T>, для старых есть готовые библиотеки, здесь я давал ссылки:
http://delphimaster.net/view/1-1383011872/
← →
Павиа (2013-11-07 06:24) [17]The eight bytes before the location contain a 32-bit length indicator and a 32-bit reference count. This memory is allocated on the heap, but its management is entirely automatic and requires no user code.
Вот из справки так что согласно справки заботиться не надо.
← →
Павиа (2013-11-07 06:26) [18]Единственное что так нельзя делать, так это размножать строки.
← →
DevilDevil © (2013-11-07 09:58) [19]> Sapersky (07.11.13 04:46) [16]
> Для финализации конкретного типа есть стандартная Finalize,
> которая работает как compiler magic, сама подставляет TypeInfo:
>
> Finalize(Arr[Index]);
Да
Ты прав!
Я думал, только в новых версиях Delphi
С другой стороны если финализировать несколько элементов подряд - моя функция быстрее, чем цикл стандартных Finalize()
← →
jack128_ (2013-11-07 11:49) [20]
> С другой стороны если финализировать несколько элементов
> подряд - моя функция быстрее, чем цикл стандартных Finalize()
во первых стандартный Finalize вторым параметром кол-во элементов принимает,а во вторых даже если бы и не принимал, то не видно за счет чего твоя функция будет быстрее.
← →
DevilDevil © (2013-11-07 12:34) [21]> jack128_ (07.11.13 11:49) [20]
> во первых стандартный Finalize вторым параметром кол-во
> элементов принимает,а во вторых даже если бы и не принимал,
> то не видно за счет чего твоя функция будет быстрее.
стандартный Finalize не способен охватить диапазон например с 4 по 7 элементы массива. Придётся идти итерацией по одному вызову стандартного Finalize, который в свою очередь приводит к ранее указанному вызову FinalizeArray с размером 1
← →
jack128_ (2013-11-07 13:20) [22]
> стандартный Finalize не способен охватить диапазон например
> с 4 по 7 элементы массива
Finalize(arr[4], 4);
> по одному вызову стандартного Finalize, который в свою очередь
> приводит к ранее указанному вызову FinalizeArray с размером
> 1
как раз таки если ты не задаешь количество элементов, то FinalizeArray вообще не будет вызываться.
← →
DevilDevil © (2013-11-07 14:05) [23]> jack128_ (07.11.13 13:20) [22]
> Finalize(arr[4], 4);
Да! Ты полностью прав! Признаю свою ошибку!
> как раз таки если ты не задаешь количество элементов, то
> FinalizeArray вообще не будет вызываться.procedure _Finalize(p: Pointer; typeInfo: Pointer);
{$IFDEF PUREPASCAL}
begin
_FinalizeArray(p, typeInfo, 1);
end;
{$ELSE}
asm
MOV ECX,1
JMP _FinalizeArray
end;
{$ENDIF}
← →
jack128_ (2013-11-07 16:17) [24]ты по отладчиком смотри что вызывается.
← →
DevilDevil © (2013-11-07 16:40) [25]> jack128_ (07.11.13 16:17) [24]
> ты по отладчиком смотри что вызывается.
В Delphi 6-7 вызывается System.Finalize, который вызывает System.FinalizeArray(, 1)
← →
jack128_ (2013-11-07 23:01) [26]ну значит они типа "прооптимизировали" этот момент.
на xe4
var
x: record s: string; end;
begin
Finalize(x);
end;
вызывает FinalizeRecord/ если x - string, то StrClr . И так далее по всем типам.
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.003 c