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

Вниз

Динамические массивы   Найти похожие ветки 

 
DeadMeat   (2008-11-21 13:58) [0]

Здравсте.
Есть вопрос по поводу динамических массивов.
Представим, что у нас есть два динамических массива, такого вида:
A1: array of byte;
A2: array of single;
Задача состоит в том, чтобы передав их в определенную процедуру, изменить их длинну и "вернуть назад".
Т.е. нужна некая процедура, которая принимает на вход динамические массивы любого типа, либо поинтер на них, либо еще что. Результатом ее действия должен быть увеличенный на N элементов (или байт, если по другому не получится) переданный ранее массив.
Нечто вроде:
procedure Reallocate(....; const ElementSize: integer);
begin
 .... // увеличение длинны массива на ElementSize
end;
Если я правильно понял, то передать любой массив можно через Open Arrays, но по поводу них справка четко говорит, что менять его длинну в процедуре нельзя.
Пробовал передать просто поинтер, выделить в нем память (GetMem) размером с ElementSize байтов, скопировать в новое место данные из старого (типа того, что делает SetLength) и вернуть новый поинтер на место первого, но память выделяется не понятно как. Т.е. смотрю в дебагер после этих действий и вижу, что на месте этого массива получился всякий мусор. Не в том смысле, что там данные "левые", а в том, что.... Даже не знаю как объяснить. Ну допустим я выделил (точнее хотел выделить) 10 элементов, каждый по 720 байт (нечто типа array of TMyRecord, размер которого 720 байт). В результате в дебагере вижу, что первые два элемента выделились, другие два - нет и т.п. "фигня".
Вообщем суть вопроса. Как сделать так, чтобы в процедуру можно было передать динамический массив любого типа, изменить его размер и "вернуть назад", т.е. чтобы по выходу из этой процедуры, этим массивом можно было пользоваться, записывая данные в его "новые" места.


 
tesseract ©   (2008-11-21 14:00) [1]

Драсте вам, передавай ссылку и размер массива в процедуру. Всё выйдет.


 
{RASkov} ©   (2008-11-21 14:18) [2]

А вот прямо просто SetLength не подойдет разве?


 
DeadMeat   (2008-11-21 14:19) [3]


> tesseract ©   (21.11.08 14:00) [1]

Не совсем понял как это. Делал варианты типа:
procedure Reallocate(Buf: array of const);
procedure Reallocate(var Buf: array of const);
procedure Reallocate(const Buf: array of const);
Даже делал реальный тип массива, который передаю (array of byte пробовал).
Все равно на строке SetLength(Buf, 10) выдает "Incompatible types".


 
Плохиш ©   (2008-11-21 14:22) [4]


> DeadMeat   (21.11.08 13:58)

Open Array и Dinamic Array две разные сущности и имеют общее только одно слово в названии...


 
Сергей М. ©   (2008-11-21 14:29) [5]


> Как сделать так, чтобы в процедуру можно было передать динамический
> массив любого типа


Любого не получится - тип должен быть известен и вызывающей и вызываемой процедуре.


 
Сергей М. ©   (2008-11-21 14:31) [6]


> вернуть новый поинтер на место первого, но память выделяется
> не понятно как


Показывай код ..


 
Anatoly Podgoretsky ©   (2008-11-21 14:54) [7]

> DeadMeat  (21.11.2008 13:58:00)  [0]

Ты вроде в школе не учишься, а задание школьное или все таки учишься?


 
Sapersky   (2008-11-21 15:01) [8]

http://sapersky.narod.ru/files/Arrays.rar


 
DeadMeat   (2008-11-21 15:17) [9]


> Сергей М. ©   (21.11.08 14:31) [6]

Не покажу... ))))
На самом деле я столько экспериментировал, чтобы получилось без извращений, что щас уже и не восстановлю все.
Сейчас сделал так:

type
 TMyRecord = record
   public
     var
       Data: integer;
       NewData: word;
       AnotherData: single;

     procedure Init(const Val: integer);
 end;
.....
{ TMyRecord }

procedure TMyRecord.Init(const Val: integer);
begin
 Data        := Val + 0;
 NewData     := Val + 1;
 AnotherData := Val + 2;
end;
.....
function Reallocate(const Buf: pointer; const Count, ElementSize: integer): pointer;
type
 PByteArray = ^TByteArray;
 TByteArray = array of byte;
var
 bufData: array of byte;
 bufRelocArray: PByteArray;
begin
 SetLength(bufData, Count * ElementSize);
 MoveMemory(@bufData[0], Buf, Count * ElementSize);

 New(bufRelocArray);
 SetLength(bufRelocArray^, Count + 1);
 MoveMemory(@bufRelocArray^[0], @bufData[0], Count * ElementSize);
 Result := bufRelocArray^;
end;
.....
var
 Buffer: array of TMyRecord;
begin
 SetLength(Buffer, 1);
 Buffer[0].Init(1);

 Buffer := Reallocate(Buffer, Length(Buffer), SizeOf(TMyRecord));
 Buffer[1].Init(2);

 Buffer := Reallocate(Buffer, Length(Buffer), SizeOf(TMyRecord));
 Buffer[2].Init(3);
end;
.....

Вроде работает, но гложат меня сомнения, что я гдето либо теряю память, либо вообще все делаю не так.


> Anatoly Podgoretsky ©   (21.11.08 14:54) [7]

Можем считать, что я вернулся в школу.. ;))


> Sapersky   (21.11.08 15:01) [8]

Пасиба.. обязательно посмотрю.


 
Сергей М. ©   (2008-11-21 15:33) [10]


> Не покажу


Стыдно чтоль стало ?

И ведь судя по коду в [9] есть за что - замесил в одну кучу и прямую работу с менеджером и дин.массивы ..


 
Amoeba ©   (2008-11-21 15:36) [11]


> Вроде работает, но гложат меня сомнения, что я гдето либо
> теряю память, либо вообще все делаю не так.
>

Верно последнее: все делаешь не так.


 
Sapersky   (2008-11-21 15:44) [12]

Есть внутренняя функция DynArraySetLength, которая задаёт размер дин. массива независимо от типа. Начиная с D6 её "легализовали", т.е. вынесли в interface system.pas (хотя при желании её можно использовать и в более ранних версиях - см. исходник по ссылке).
TypeInfo получаем как TypeInfo(тип массива).


 
DeadMeat   (2008-11-21 15:49) [13]


> Sapersky   (21.11.08 15:44) [12]

Вот собсна, только благодаря Вашему примеру, я про не узнал. Большое спасибо. Проблема стала почти решена.


> Сергей М. ©   (21.11.08 15:33) [10]
> Amoeba ©   (21.11.08 15:36) [11]

Аййй.... больно... но не смертельно.. )) Как бы выяснить, что конкретно там не верно?


 
Плохиш ©   (2008-11-21 15:59) [14]


> Sapersky   (21.11.08 15:44) [12]
>
> Есть внутренняя функция DynArraySetLength,

Чем "невнутренняя" функция, озвученная в [2] не нравиться?


 
DeadMeat   (2008-11-21 16:07) [15]


> Плохиш ©   (21.11.08 15:59) [14]

Ну по крайней мере, я не нашел способа как ее применить конкретно в этой ситуации...
Признаю, что наверняка плохо искал..
Есть ли таковой?


 
Sapersky   (2008-11-21 16:17) [16]

Вот собсна, только благодаря Вашему примеру, я про не узнал. Большое спасибо. Проблема стала почти решена.

Там и увеличение размера есть (Arr_Grow). Правда, не насколько-то, а сразу в 2 раза, но это легко изменить.

Ну по крайней мере, я не нашел способа как ее применить конкретно в этой ситуации...
Признаю, что наверняка плохо искал..
Есть ли таковой?


Ну можно использовать и SetLength, если откастить массив к array of Byte, но это "некрасивый" метод, и при наличии в массиве длинных строк и т.п. managed-типов можно огрести глюков.
И кстати, SetLength для массива преобразуется компилятором в DynArraySetLength с подстановкой нужного типа, так что в любом случае мы используем DynArraySetLength.


 
Сергей М. ©   (2008-11-21 16:22) [17]


> DeadMeat   (21.11.08 15:49) [13]


Ну а зачем вообще внутри ф-ции, явно взаимодействующей с мкнеджером пямяти, использовать манипуляции с дин.массивами ?


 
Sapersky   (2008-11-21 16:35) [18]

Кстати о менеджерах памяти. При наличии FastMM не обязательно оптимизировать наращивание массива, можно тупо писать SetLength(Arr, Length(Arr) + 1) :)
Все оптимизации делает сам менеджер.
http://delphimaster.net/view/1-1226920476/


 
DeadMeat   (2008-11-21 16:55) [19]


> Sapersky   (21.11.08 16:17) [16]

Ну это все понятно. Я это и затеял для того, чтобы сделай некое подобие TList у себя. Как раз для наращиваний массивов не по одному. И тему ту читал..


> Сергей М. ©   (21.11.08 16:22) [17]

Эмм... да вот хотелось получить решение, удобное в использовании. Вот и кастил туда-сюда типы. Но то, чего я хотел, не получилось... Будем юзать то, что показал мне товарищ Sapersky.


 
Сергей М. ©   (2008-11-21 17:03) [20]


> хотелось получить решение, удобное в использовании


Откажись от дин.массивов вообще - будет чуть менее удобно, но зато универсально.

Да и вообще не оч понятно, за каким лешим нужно передавать куда-то дин. массив (да и не только динамический), только лишь для того чтобы увеличить его размерность.. Почему это нелья сделать прямо в вызывающем коде ?


 
DeadMeat   (2008-11-21 17:27) [21]


> Сергей М. ©   (21.11.08 17:03) [20]

Ну... отказаться не могу. Размер данных я заранее не знаю... юзать для этого статические массивы со своими счетчиками будет расточительно, т.к. размеры данных будут не маленькими.
А передевать.... есть такое у меня желание. Не создавать свой класс для списков, а использовать везде обычные массивы, но для наращивания вызывать одну функцию, которая будет делать это с запасом.
Отлично понимаю, что сделать свой класс будет на много удобно, но условия у меня чуто специфичные.


 
Сергей М. ©   (2008-11-21 18:07) [22]


> DeadMeat   (21.11.08 17:27) [21]


> юзать для этого статические массивы


Кто сказал про статику ?

Ни о какой статике не идет и речи ..


 
DeadMeat ©   (2008-11-21 18:49) [23]


> Сергей М. ©   (21.11.08 18:07) [22]

О_о
Тогда о чем? При условии использования только "своего". В том смысле, что всякие "вспомогательные" классы, типа TList не подходят.
Просто выделять память через GetMem ? Но мне надо обращаться к этим элементам, которые будут записаны.. Мне их надо читать, обрабатывать и т.п.
Я в замешательстве...


 
Sapersky   (2008-11-21 19:26) [24]

Может, речь идёт о конструкциях вроде:

Type
 PByteArr = ^TByteArr;
 TByteArr = array [0..0] of Byte;
Var
 Arr : PByteArr;

Память выделяется через GetMem/FreeMem, можно обращаться к элементам по индексу.
Но фактически это менее удобно чем дин. массивы:
1) Содержимое при отладке не видно;
2) Автоматически не уничтожается;
3) Непонятно что делать при наличии в массиве managed-типов, скорее всего, придётся инициализировать/прибивать вручную.
Одна радость - скомпилируется в D1-3 и вроде даже в TP :)

А, нет, есть одно достоинство - позволяет делать двумерные массивы, лежащие в памяти "одним куском". Ну или обращаться к произвольному блоку памяти как к двумерному массиву (картинке, например).
Многомерные дин. массивы представляют собой массивы указателей, реальные данные начинаются только в последнем измерении. Это не всегда удобно и несколько замедляет доступ к элементу.


 
Сергей М. ©   (2008-11-21 19:30) [25]


> всякие "вспомогательные" классы, типа TList не подходят


Понятно - Коран запрещает.


> Просто выделять память через GetMem ?


Почему бы и нет ?


> Но мне надо обращаться к этим элементам, которые будут записаны.
> . Мне их надо читать, обрабатывать и т.п.


Да на здоровье !
Коран же не запрещает)..

И Делфи тоже, кстати.


 
DeadMeat   (2008-11-21 20:42) [26]


> Сергей М. ©   (21.11.08 19:30) [25]

Хех... Ну про удобства уже сказали чуть выше. В принципе я согласен. Для меня както динамические массивы удобней остальных методов. А теперь практически совсем удобны. Осталась пара нюансов, но уже не таких существенных.
Однаков все-же один вопрос для меня остался не открытым. Как можно используя SetLength реализовать [0] ?


 
Sapersky   (2008-11-21 21:12) [27]

// Arr is Delphi dymamic array (array of ...)
// array elements MUST NOT contain elements with automatic memory management,
// i.e. long strings, variants etc.
// this proc may not work in future Delphi versions
// (if dymamic array format will change)

procedure Mem_GrowArray(Var Arr; NewCount, ElSize : Integer);
Type
 TByteArr = array of Byte;
Var
 Capacity : Integer;
 PLength : PInteger;
begin
PLength := PInteger(Arr);
If (PLength <> nil) then begin
 Dec(PLength);
 If (NewCount <= PLength^) then Exit; // we fit to existing capacity, no need to grow

 Capacity := PLength^ * 2;
 If Capacity < NewCount then Capacity := NewCount;

 PLength^ := PLength^ * ElSize;
   // as we cast array to byte array, we need to temporarily convert
   // its length in bytes for correct reallocation
end else
 Capacity := NewCount; // was no array - just allocate NewCount elements

SetLength(TByteArr(Arr), Capacity * ElSize);

If (Capacity > 0) then begin
 PLength := PInteger(Arr); Dec(PLength);
 PLength^ := Capacity; // set length in elements
end;
end;


 
Сергей М. ©   (2008-11-21 21:19) [28]


> Как можно используя SetLength реализовать [0] ?


Т.е. это самое что ни на есть принципиальное ослиное ничем не аргументированное упрямство ?)


 
DeadMeat   (2008-11-21 21:23) [29]


> Sapersky   (21.11.08 21:12) [27]

Ну это почти то, что делал я. Разница конечно есть, но смысл тот же. Каст и ресайз.
Вопрос именно в том, есть ли чуток более стандартный способ.. Просто посты из [1], [2] и [14] наталкивают меня именно на эту мысль.. Что я чтото упустил из стандартного... может синтаксис какой не знаю..


 
DeadMeat   (2008-11-21 21:24) [30]


> Сергей М. ©   (21.11.08 21:19) [28]

Ну собсна да... хоца знать все новое. ))


 
Amoeba ©   (2008-11-22 00:51) [31]


> Как можно используя SetLength реализовать [0] ?

Все еще Гондурас чешется?


 
DeadMeat   (2008-11-22 10:27) [32]


> Amoeba ©   (22.11.08 00:51) [31]

Ну тогда мне совершенно не понятны [1], [2] и [14]
Может таки есть способ, раз люди о нем намекнули.. По крайней мере мне так показалось, что намекнули..


 
sniknik ©   (2008-11-22 11:18) [33]

> Может таки есть способ
а [27] разве не способ?


 
DeadMeat   (2008-11-22 11:44) [34]

Мда.. По всей видимости мы друг-друга не понимаем. Ну и ладно...
Всем спасибо за помощь.


 
Amoeba ©   (2008-11-23 00:17) [35]


> DeadMeat   (22.11.08 11:44) [34]
>
> Мда.. По всей видимости мы друг-друга не понимаем. Ну и
> ладно...
> Всем спасибо за помощь.

Все еще чешется ... Ну и Господь с тобой.


 
Германн ©   (2008-11-23 01:05) [36]


> sniknik ©   (22.11.08 11:18) [33]

Повторить твой незабываемый пример с "переходом дороги для покупки хлеба" не хочешь?

> Задача состоит в том, чтобы передав их в определенную процедуру,
>  изменить их длинну и "вернуть назад".
>

:)
P,S. <offtop>
Можно пойти по пути Palladin.
</offtop>
P.P.S.  Вот очень желательно иметь под рукой нечто, чтобы объяснить автору отличие "технического вопроса" от "описания задачи".


 
sniknik ©   (2008-11-23 02:06) [37]

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


 
Германн ©   (2008-11-23 04:39) [38]


> в каждую вторую ветку вставлять? а то и чаще.

В каждую первую!. :)
Ну нужно иметь под рукой что-то "художественное" для ответов.
Твой пример второй. Первый "художественный" ответ был дан ЮЗ. Его я то же бы попросил бы опубликовать где-то на данном форуме. Это ответ "телефонная книжка".
Где именно? Ну у нас на форуме есть две "скрытые" конференции. Плюс "новая" версия форума.



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

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

Наверх




Память: 0.58 MB
Время: 0.016 c
2-1227547166
Res
2008-11-24 20:19
2009.01.04
gethostbyname


15-1225429373
Slider007
2008-10-31 08:02
2009.01.04
С днем рождения ! 31 октября 2008 пятница


15-1225574351
er-acer
2008-11-02 00:19
2009.01.04
Патент программы с MySQL


2-1227619523
snake-as
2008-11-25 16:25
2009.01.04
Работа с файлами


15-1225800022
El
2008-11-04 15:00
2009.01.04
Баннеры