Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 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.002 c
15-1383748436
SergP
2013-11-06 18:33
2014.05.04
Динамический массив записей со строками. подскажите


15-1383253856
Пит
2013-11-01 01:10
2014.05.04
Мышка слишком быстрая


1-1324995516
Dark King
2011-12-27 18:18
2014.05.04
Компилятор


15-1383220486
DevilDevil
2013-10-31 15:54
2014.05.04
СТРОКИ НЕ ПОТОКОБЕЗОПАСНЫ


15-1383488747
Интересующися
2013-11-03 18:25
2014.05.04
Сдохла справка. D2010, XE5





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский