Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2009.01.04;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.008 c
15-1225807517
Petr V. Abramov
2008-11-04 17:05
2009.01.04
Электронные деньги - будущая замена бумажных денег?


15-1225662065
Сатир
2008-11-03 00:41
2009.01.04
США начинают информационную войну в Рунете


15-1225721457
Xenus
2008-11-03 17:10
2009.01.04
Форум работает с перебоями


4-1203344424
Cj
2008-02-18 17:20
2009.01.04
SetFileSecurity


15-1225510312
Slider007
2008-11-01 06:31
2009.01.04
С днем рождения ! 1 ноября 2008 суббота





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский