Работа с динамическими массивами

 
0
 
Delphi, Kylix & Pascal
ava
EKoshelev | 21.03.2013, 16:30
Доброго дня.

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

Чуть конкретнее.
Массивы будут создаваться по стандартной схеме:
arr1: array of integer;
arr2: array of TObject;
arr3: array of record
  a, b, c: double;
  b: array [0..4] of boolean;
end;
Далее я буду передавать arr1, arr2 и arr3 в какие-то функции, и с ними будут происходить необходимые преобразования. При этом во всех трёх случаях, например, для добавления элемента я буду вызывать одну и ту же процедуру.

В принципе, мне почти всё понятно за исключением одного момента. Не понятно как изменить длину массива.

Дело в том, что если массивы с разными элементами будут передаваться в одну процедуру, то процедура должна будет воспринимать как указатели (по крайней мере, лично я других вариантов не вижу). При этом воспользоваться функцией SetLength я не смогу (хотя вот прямо сейчас подумал, что можно попробовать извратиься). Короче, если бы знать как изменить длину массива не используя SetLength, то, вероятно, все мои проблемы решились бы.

Короче, если кто-то может чем-то помочь, то я был бы благодарен. Ну, естественно, если уже есть решения, то ссылки были бы не лишними.


Заранее спасибо.
Comments (9)
ava
AndreyIQ | 21.03.2013, 15:47 #
Вам точно массив нужен? Может TList проще будет?
ava
EKoshelev | 21.03.2013, 15:52 #
AndreyIQ, Ну а как без напрягов в TList'е можно хранить int64 или структуры? Если только по ссылкам. При этом (если я ошибаюсь - поправь) постоянно нужно будет преобразовывать типы. Я как-то (с помощью TList) накидал класс хранящий interger'ы, и вместо Pointer'ов хранил integer'ы. Получилось удобно, но не скоро. Каждый раз писать новый класс для нового типа - не охота и долго. А при таком подходе, полагаю, можно всё будет сделать довольно быстро.
ava
AndreyIQ | 21.03.2013, 16:04 #
Начиная с Delphi 2009 (если не ошибаюсь) появилась поддержка дженериков. Очень удобно в использовании.
ava
Чучмек | 21.03.2013, 19:42 #
Цитата (EKoshelev @  21.3.2013,  15:30 findReferencedText)
При этом воспользоваться функцией SetLength я не смогу 

Почему?

type
TArrayType=(atByte,atWord,atInt,atString);
TByteArray=array of Byte;
TWordArray=array of Word;
TIntArray=array of Integer;
TStringArray=array of String;

procedure MySetLength(var m;length:integer;ArrayType:TArrayType);
begin
case ArrayType of
  atByte:   SetLength(TByteArray(m),length);
  atWord:   SetLength(TWordArray(m),length);
  atInt:    SetLength(TIntArray(m),length);
  atString: SetLength(TStringArray(m),length);
end;
end;


var
m:array of integer;
begin
MySetLength(m,32,atInt);
Caption:=inttostr(length(m));
ava
CynicRus | 21.03.2013, 20:39 #
Вот кусочек кода  библиотеки Fundamentals, содержит функции для работы с динамическими массивами. Правда автор использовал не указанный выше механизм, а перегрузку функций. 
ava
EKoshelev | 22.03.2013, 07:49 #
AndreyIQ, Спасибо. Не знал. Можно будет глянуть.


ЧучмекCynicRus, Ребята, ну вы предлагаете рассматривать массивы с элементами известных типов. Мне бы хотелось уметь работать с массивами элементов любых типов. Даже неизвестных заранее.

На сколько я сумел выяснить на данный момент, динамический массив - это некая структура.
Если у нас есть массив Arr: array of SmthType, то
Pointer(Arr) указывает на первый (или нулевой) элемент массива
integer(Pointer(Arr - 4)) хранит ссылку на переменную с к-вом элементов массива
integer(Pointer(Arr - 12)) хранит ссылку на к-во байтов занимаемое массивом минус четырнадцать.
Т. е. из двух последних значений можно получить sizeof элемента массива (SizeOf(SmthType)).

Короче, для изменения длины массива, вероятно, нужно как-то изменить эту структуру. И вот хотелось бы знать как.
ava
EKoshelev | 22.03.2013, 08:19 #
Короче, зная о структуре то, что уже я знаю изменение длины массива можно (казалось бы) сделать так:
1. Создать при помощи GetMem фрагмент памяти нужной длины.
2. Перенаправить на него Pointer хранящийся по адресу Pointer(Arr)
3. Изменить длину в ячейке Pointer(Arr - 4);
4. Изменить длину в ячейке Pointer(Arr - 12);
5. И (!) освободить старый массив (FreeMem).

Вот на пятом пункте вылетает ошибка: "Invalid pointer operation".
ava
bems | 22.03.2013, 08:31 #
а что ты отдаешь в GetMem? память выделяется одни махом и на сам массив и на служебную информацию, соответственно освобождать нужно также. Еще прими во внимание что никто тебе не обещал структура по отрицательному смещению от нулевого элемента будет стабильной между версиями дельфи (например с некоторых пор там есть счетчик ссылок как у строк, хотя раньше небыло).
При таких исследованиях сильно помогает чтение исходников юнита System (ну и прочих внутренностей rtl), ты это делал?
И вообще, юзай дженерики и не занимайся извращениями
ava
bems | 22.03.2013, 08:54 #
щас посмотрел system.pas от ХЕ2 и увидел что вот это
Цитата (EKoshelev @  22.3.2013,  08:19 findReferencedText)
Изменить длину в ячейке Pointer(Arr - 12);

неправильно
на х64 Arr - 12 используется просто для выравнивания, а на х32 служебная структура динамического массива занимает всего 8 байт (два четырехбайтовых целых Arr - 4 и Arr - 8)
Если ты действительно увидел что по смещению -12 лежит поинтер на длину в байтах, то вероятно служебные данные менеджера памяти, и относятся они не к самому массиву, а к блоку памяти, как его знает менеджер. Короч не нужно освобождать 12 байт перед массивом, нужно 8
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
advanced
Submit