Форум: "Основная";
Текущий архив: 2004.02.17;
Скачать: [xml.tar.bz2];
ВнизРабота с динамической памятью и указателями Найти похожие ветки
← →
Lucky[ELF] (2004-02-01 23:04) [0]Доброго времени суток! Подскажите пожалуйста как работать с динамической памятью.
Вместо предисловия: я пишу на разных языках и так уж случилось, что Си я знаю лучше всего, но хотелось бы иметь навыки в Дельфях не меньшии. Работу со списками я начал изучать с Паскаля, поскольку так решил препод, параллельно я делал это на Сях, по выше указанной причине. До сих пор со списками проблем не было ни в одном из двух языков. А не так давно я писал динамическое хранилище на С++ - написал и все получилось и тут у меня всал вопрос как такое же реализовать на Пасе/Делфях?
Вопрос конкретно в следующем:
на Сях/С++ я написал такую функцию
int CVault ::CreateItem( void *pData, word dSize, eVaultType_t vType )
{
VaultItem_t *pItem, *newItem = new( VaultItem_t );
if ( !newItem ) // Ошибка, неудалось выделить память
return -1;
newItem->data = (byte *)new byte [dSize]; // Выделяем место под данные
if ( !newItem->data ) // Если память выделеть не удалось
{
delete newItem; // Освобождаем то под что, выделить удалось
return -1; // Выход с ошибкой
}
memcpy( newItem->data, pData, dSize ); // Копируем данные
newItem->dataSize = dSize; // Запоминаем размер данных
newItem->ID = lastID++; // Генерим ID
newItem->next = NULL; // Следующего пока нет
// Подцепляем созданный элемент к списку
if ( !rootItem )
rootItem = newItem;
else
{
if ( !vType ) // Вставка в начало списка
{
// Реализация списка
newItem->next = rootItem;
rootItem = newItem;
}
else // Вставка в конец списка
{
// Реализация очереди
pItem = rootItem;
while( pItem->next != NULL )
pItem = pItem->next;
pItem->next = newItem;
}
}
maxItem++; // Счетчик очереди надо увеличить
return newItem->ID; // Все прошло успешно
}
Вот далее для ее использования
vMainMenu.CreateItem (&newItem, sizeof (newItem));
поясню тут:
newItem
- область памяти которую нужно сохранить, это может быть все что угодно - массив, структура, объединение, переменная.
sizeof (newItem)
- соответсвенно объем этих данных
Вопрос как реализовать это на Дельфях?
т.е. конкретные места которые у меня вызываеют непонимание - это как передать любые данные в функцию и как эти данные скопировать в выделенную память? и еще чем выделять память
procedure New(var P: Pointer);
procedure Dispose(var P: Pointer);
или
procedure FreeMem(var P: Pointer[; Size: Integer]);
procedure GetMem(var P: Pointer; Size: Integer);
или есть еще какие нибудь?
Заранее спасибо! С уважением Lucky[ELF]!
ЗюЫю можно ссылки на ресурсы.
← →
Юрий Зотов (2004-02-02 00:40) [1]Вот пример построения классического связанного списка (писал прямо здесь и не проверял, поэтому возможны ошибки, но для того, чтобы показать принципы, думаю, это неважно).
type
PItem = ^TItem;
TItem = record // или packed record
... // произвольный набор полей
NextItem: PItem
end;
var
FirstItem: PItem = nil;
procedure CreateNewItem;
var
LastItem: PItem;
begin
// Если списка еще нет, то создаем его первый элемент
if FirstItem = nil then
begin
New(FirstItem);
FirstItem^.NextItem := nil; // Признак последнего элемента
Exit
end
// Ищем последний элемент списка
LastItem := FirstItem;
while LastItem^.NextItem <> nil do
LastItem := LastItem^.NextItem;
// Создаем новый элемент и добавляем его в конец списка.
// Этому новому элементу ставим признак последнего.
New(LastItem^.NextItem);
LastItem^.NextItem^.NextItem := nil
end;
procedure DeleteAllItems;
var
P: PItem;
begin
while FirstItem <> nil do
begin
P := FirstItem^.NextItem;
Dispose(FirstItem);
FirstItem := P
end
end;
← →
Lucky[ELF] (2004-02-02 21:58) [2]Мдя! так и думал, что буду не понят. мне надо передать в процедуру указатель на массив или запись, потом выделить под это память и скопировать туда.
На сях я делал это так.
int CVault ::CreateItem( void *pData, word dSize, eVaultType_t vType )
{
VaultItem_t *pItem, *newItem = new( VaultItem_t );
if ( !newItem ) // Ошибка, неудалось выделить память
return -1;
newItem->data = (byte *)new byte [dSize]; // Выделяем место под данные
if ( !newItem->data ) // Если память выделеть не удалось
{
delete newItem; // Освобождаем то под что, выделить удалось
return -1; // Выход с ошибкой
}
memcpy( newItem->data, pData, dSize ); // Копируем данные
← →
jack128 (2004-02-02 22:10) [3]короче прямой перевод на Object pascal
functiom CVault.CreateItem(pData: Pointer;dSize: word; vType: eVaultType_t): integer;
var
pItem, newItem: ^VaultItem_t;
begin
New(newItem);
if newItem = nil then // Ошибка, неудалось выделить память
begin
Result := -1;
Exit;
end;
newItem^.data = AllocMem(dSize); // Выделяем место под данные
if newItem^.data <> nil then // Если память выделеть не удалось
begin
Dispose(newItem); // Освобождаем то под что, выделить удалось
Result := -1; // Выход с ошибкой
end;
memcpy(newItem^.data, pData, dSize ); // Копируем данные
← →
jack128 (2004-02-02 22:14) [4]мда. Что то мя глюкнуло
вместо memcpy используй Move.
Move(pData^, newItem^.Data^, dSize);
← →
Lucky[ELF] (2004-02-02 23:43) [5]Вот это уже лучше, обязательно попробую. Спасибо.
А есть ли возможность добраться до до определенной ячейки -ну типа как в массиве, сишка это дело понимает просто arr[index], а как такое реализуется в Дельфях?
А Move он копирует или перемещаяет?
И последнее как будет выглядет вызов этой функции?
ItemID := CVault.CreateItem (@vRec, sizeof(vRec), eList );
инересует момент передачи указателя - надо писать @ или нет? или это как-то по другому?
Спасибо! с уважением!
← →
jack128 (2004-02-02 23:50) [6]
> добраться до до определенной ячейки -ну типа как в массиве,
> сишка это дело понимает просто arr[index], а как такое реализуется
> в Дельфях
если arr - массив, то точно так же.
> А Move он копирует или перемещаяет?
а это не одно и тоже? ;-)
> И последнее как будет выглядет вызов этой функции?
>
> ItemID := CVault.CreateItem (@vRec, sizeof(vRec), eList
> );
именно так и выглядит.
И последнее: завтра, с утра идешь в магазин и покупаешь книжку по языку Object Pascal ;-)
← →
Юрий Зотов (2004-02-03 12:38) [7]> Lucky[ELF]
Я думал, что у Вас есть вопросы по принципам. Оказалось, что они у Вас всего лишь по синтаксису. Сорри, но это несерьезно.
P.S.
За исключением одного вопроса - Move он копирует или перемещает. Который и не по синтаксису, и не по принципам, а по азбуке.
← →
Lucky[ELF] (2004-02-03 18:58) [8]
> jack128 © (02.02.04 23:50) [6]
>
> > добраться до до определенной ячейки -ну типа как в массиве,
>
> > сишка это дело понимает просто arr[index], а как такое
> реализуется
> > в Дельфях
> если arr - массив, то точно так же.
Нет это не массив, arr - это указатель на область памяти, например как это ->
newItem^.data = AllocMem(dSize); // Выделяем место под данные
В том то и прикол, что в Сях выделив память динамически можно "перелапатить" ее как массив, интересно как это выполнить на пасе, на сишке также это можно делать и через указатель (*pMem + x <-> pMem[x]) - в пасе должно быть что-то тоже.
> > А Move он копирует или перемещаяет?
> а это не одно и тоже? ;-)
Смутило название :), хотя ести вспомнить ASM, то там сплошь и рядом mov :) и как я мог забыть
> И последнее: завтра, с утра идешь в магазин и покупаешь
> книжку по языку Object Pascal ;-)
Ни за что! тем более по пасу! причина - просто не вижу особой необходимости, хотел было купить книгу по Delphi7+DataBase, но и на ту денег зажал ("еврейская" натура взяла свое).
нет надобности по той причине, что обычно пишу на Сях, но иногда требуется именно на Дельфях, а так как привых к Сям, то привык к его возможностям - и не знаю как некоторые выполнить на Пасе (память и указатели как раз одно из)
"Язык формирует наш способ мышления и определяет, о чем мы можем мыслить."
- Б.Л. Ворф
> Юрий Зотов © (03.02.04 12:38) [7]
> > Lucky[ELF]
>
> Я думал, что у Вас есть вопросы по принципам. Оказалось,
> что они у Вас всего лишь по синтаксису. Сорри, но это несерьезно.
Напрасно вы думаете, что это не серьезно, подобно тому как ... (см. цитату) в языке еще ОЧЕНЬ важно знать, что МОЖНО и как это выполнить а иначе никуда - это еще на первом курсе произнес мой учитель по asm"у, но я тогда тупо смотря на доску не обратил на нее внимания! :)
> P.S.
> За исключением одного вопроса - Move он копирует или перемещает.
> Который и не по синтаксису, и не по принципам, а по азбуке.
:))))
Моя глупость, моя.
Спасибо за разъяснения! так глядишь и ума наберусь.
С уважением Lucky[ELF]
← →
Romkin (2004-02-03 19:07) [9]А в Delphi обычно очень мало работают с динамической памятью явно.
string & dynamic array рулят.
Например, объявляешь
type
TMyArray = array of integer;
var
MyArray: TMyArray;
begin
SetLength(MyArray, 300); //Все, есть массив от 0 до 299
//работаем с ним
end; //А вот уничтожится он, как только выйдет из области видимости
То же самое со string, только у них еще и счетчик копий есть
← →
Lucky[ELF] (2004-02-03 19:57) [10]
> Romkin © (03.02.04 19:07) [9]
Вот это вообще новости! обязательно возьму на заметку, так все просто, что аж не по себе!
А если дальше этот массивчик передать надо в процедуру или функцию или вернуть тогда как быть?
← →
Romkin (2004-02-03 20:04) [11]Да я тут сегодня в Медии писал буквально следующее:
TCurveArray = array of TPoint;
function TCurves.PointArray(Index: integer): TCurveArray;
var
Curr: PCurve;
i, len: integer;
begin
Result := nil;
Curr := GetCurve(Index);
len := 0;
while assigned(Curr) do
begin
inc(len);
Curr := Curr^.Next;
end;
if len = 0 then exit;
SetLength(Result, len); //фактически выделяем память
Curr := GetCurve(Index);
for i := High(Result) downto Low(Result) do
with Result[i] do
begin
X := Curr^.X;
Y := Curr^.Y;
Curr := Curr^.Next;
end;
end;
//юзаем:
var
CurveArray: TCurveArray;
for CurveIndex := 0 to FCurves.Count - 1 do
with pbCurve.Canvas do
begin
Pen.Color := FCurves.Color[CurveIndex];
CurveArray := FCurves.PointArray(CurveIndex);
Polyline(CurveArray);
CurveArray := nil; //Параноик я, что поделать. На самом деле не нать
end;
← →
Юрий Зотов (2004-02-03 20:11) [12]> Romkin © (03.02.04 19:07) [9]
> То же самое со string, только у них еще и счетчик копий есть
У динамических массивов он тоже есть. По крайней мере, в окне CPU он наблюдается по смещению -8. А по смещению -4 наблюдается текущая длина (кол-во элементов). Все как и в строках.
Вообще, вводя в язык динамические массивы, Borland поступила очень мудро - она использовала для работы с ними те же самые механизмы, что уже были отработаны на длинных строках.
← →
Тимохов (2004-02-03 20:16) [13]
> Вообще, вводя в язык динамические массивы, Borland поступила
> очень мудро - она использовала для работы с ними те же самые
> механизмы, что уже были отработаны на длинных строках.
Если позволите уточнение?
Механизмы то теже, за исключением того, что если после копирования строк путем присвоения одной пременной другой (s1 := s2) изменить строку-копию (s1), то это не скажется на строку-прототипе (s2). А в дин. массивах изменение массива-копии, будет синхронно влиять на массив-прототип.
← →
jack128 (2004-02-03 20:26) [14]
> А в дин. массивах изменение массива-копии, будет синхронно
> влиять на массив-прототип.
Пока я писал, ты уже отметил эту особенность :-)
Непонятно, что мешало борланду ввести у массивов такое же поведение, как и у строк
← →
Тимохов (2004-02-03 20:31) [15]
> Непонятно, что мешало борланду ввести у массивов такое же
> поведение, как и у строк
Имхо это сложно. Я бы сказал не возможно. Что ты будешь делить с объектами в записях, которые являются элементами массива?
← →
jack128 (2004-02-03 20:38) [16]
> Что ты будешь делить с объектами в записях, которые являются
> элементами массива?
А что ты делаешь сейчас при выполнении
arr2 := Copy(arr1, 0, Length(arr1)); ? ;-)
← →
Юрий Зотов (2004-02-03 20:46) [17]> jack128 © (03.02.04 20:26) [14]
В строке четко известно, чем является каждый элемент - символом. А в динамическом массиве он может быть чем угодно (см. [15]). Например, еще одним динамическим массивом - и т.д. В итоге получается произвольно сложная структура и совершенно неясно, как такую ситуацию обрабатывать.
← →
Тимохов (2004-02-03 20:47) [18]В данном случае дельфи оставляет на твою ответственность такое действо. Если облажаешься, сам дурак. Я (дельфи) тут ни при чем - я тебе не давала легкую возможность бездумно копировать массивы. Если ты такой умный, что можешь разобраться с таким случаем сам - флаг в руки, я не вредная.
← →
Тимохов (2004-02-03 20:48) [19]
> В итоге получается произвольно сложная структура и совершенно
> неясно, как такую ситуацию обрабатывать.
Рекурсивно!
← →
Юрий Зотов (2004-02-03 20:51) [20]> Тимохов © (03.02.04 20:48) [19]
Это ведь всего лишь компилятор. Надо ли делать из него искуственный интеллект? Пожалуй, дороговато получится...
← →
Тимохов (2004-02-03 20:53) [21]
> Юрий Зотов © (03.02.04 20:51) [20]
Юрий!
Про рекурсию - была шутка. :)
Думаю, что посредством железных доводов, мы с Вами отстояли динамические массивы - они сделаны хорошо!
← →
jack128 (2004-02-03 20:55) [22]To Юрий Зотов ©, Тимохов ©
В крайнем случае можно ограничется разрешением присваивания массивов из элемнтарных типов.
← →
Romkin (2004-02-03 20:57) [23]Это как? Тут - присваивает, там - не присваивает? :))) Бедный программист
← →
Тимохов (2004-02-03 21:01) [24]
> jack128 © (03.02.04 20:55) [22]
Думаю вы все-таки не очень правы.
Поставте себя на место компилятора - ужас!
← →
jack128 (2004-02-03 21:02) [25]
> Это как? Тут - присваивает, там - не присваивает? :))) Бедный
> программист
нет, я имел в виду вообще запретить присваивание для дин массивов из неэлментарных типов. (например как запрещается создавать типизированные файлы из длинных строк)
← →
Юрий Зотов (2004-02-03 21:03) [26]> Тимохов © (03.02.04 20:53) [21]
Думаю, таки да, действительно хорошо. А строки, IMHO, в Delphi вообще сделаны прекрасно. По крайней мере, намного эффективнее и удобнее, чем просто ASCIIZ.
> jack128 © (03.02.04 20:55) [22]
Повторюсь - это ведь всего лишь компилятор. Надо ли делать из него искуственный интеллект? Пожалуй, дороговато получится...
← →
jack128 (2004-02-03 21:05) [27]
> Тимохов © (03.02.04 21:01) [24]
??? Что сложного? Ограничения по определенных синтаксических конструкций изходя из типов переменных в языке уже есть (см типизированные файлы из длинных строк).
Вроде распарсерить тип элементов дин массива и определить входят ли туда объекты - не так сложно.
← →
Тимохов (2004-02-03 21:09) [28]
> jack128 © (03.02.04 21:05) [27]
А вот предствте каково бы жилось кодерам.
Создал массив записей из простых типов, все круто - копируется.
Потом при модернизации проги добавил в запись не простой тип (например, объект). Нажимаешь ф9, и кирдык. Лазь потом по коду, и вставляй копирование массивов. Оно вам надо?
← →
Юрий Зотов (2004-02-03 21:12) [29]> jack128 © (03.02.04 21:05) [27]
> Вроде распарсерить тип элементов дин массива и определить
> входят ли туда объекты - не так сложно.
Не сложно, но ничего не дает. Например, на практике часто используются приемы подобного рода:
var
A: array of Integer;
...
SetLength(A, ...);
A[0] := Integer(TMyObject.Create);
← →
jack128 (2004-02-03 21:12) [30]А вот предствте каково бы жилось кодерам.
Создал типизированный файл записей из простых типов, все круто - копируется.
Потом при модернизации проги добавил в запись не простой тип (например, объект). Нажимаешь ф9, и кирдык. Оно вам надо?
Заметь я заменил лишь пару слов ;-)
← →
jack128 (2004-02-03 21:16) [31]
> A[0] := Integer(TMyObject.Create);
Некорктный пример. При явном приведении типов программист берез всю ответственность на себя. При приваивании - компилятор должен обеспечить логичное поведение операндов(не уверен, что правильно использовал слово, поясню например при a := b логично, что содержимое b копируется в a. Если по техническим причинам это сложно реализовать, то крмпилятор должен сообщить об ошибке)
← →
jack128 (2004-02-03 21:18) [32]небольшое исправление
> jack128 © (03.02.04 21:12) [30]
> А вот предствте каково бы жилось кодерам.
>
> Создал типизированный файл записей из простых типов, все
> круто - копируется.
> Потом при модернизации проги добавил в запись не простой
> тип (например, длинную строку). Нажимаешь ф9, и кирдык. Оно вам
> надо?
>
← →
Юрий Зотов (2004-02-03 21:20) [33]> jack128 © (03.02.04 21:16) [31]
Хорошо, другой пример: array of Pointer. Этот Pointer может указывать вообще на что угодно, на любую структуру данных.
← →
jack128 (2004-02-03 21:24) [34]
> Юрий Зотов © (03.02.04 21:20) [33]
и что? при приваивании логично скопировать указатели. Это что, сложно реализовать? Непонял пример...
← →
Юрий Зотов (2004-02-03 22:12) [35]> jack128 © (03.02.04 21:24) [34]
Указатель может прямо или косвенно указывать на данные с управляемым временем жизни. Как быть при копировании указателя - увеличивать счетчик ссылок на эти данные, или нет? И не менее (если не более) сложная задача - а как вообще распознать, что нетипизированный указатель ссылается на такие данные?
← →
jack128 (2004-02-03 22:32) [36]
> Юрий Зотов © (03.02.04 22:12) [35]
> Как быть при копировании указателя - увеличивать счетчик
> ссылок на эти данные, или нет?
А разве такое где то есть? Нет, конечно. Когда это копирование УКАЗАТЕЛЯ приводило к какому либо изменению самих данных?
s: string;
P1, P2: PString;
begin
P1 := @s;
P2 := P1; // Увеличение счетчика ссылок не происходит, так почему оно должно происходить при присваивании массивов array of PString??
end;
← →
Kris (2004-02-03 23:46) [37]Удалено модератором
Примечание: IMHO, уважаемый - это оффтоп...
← →
Юрий Зотов (2004-02-04 00:19) [38]> jack128 © (03.02.04 22:32) [36]
> А разве такое где то есть? Нет, конечно.
Еще как есть. Во-первых, длинные строки - это тоже самые настоящие типизированные указатели. Указывает такой адрес на запись со следующей структурой:
Смещение Содержимое
-8 Счетчик строк - ссылок на адрес со смещением 0
-4 Длина тела строки в байтах
0 Начало тела строки (кончается оно символом #0)
Для длинных строк характерен такой пример.
var
S1, S2: string;
begin
S1 := "Вася";
// Теперь S1 указывает на байт, содержащий букву "В".
// Находится этот байт в динамической памяти и
// по смещению -8 от него будет записано число 1.
S2 := S1;
// Копия тела строки не создается, а вместо этого происходит
// копирование адреса. Теперь S2 содержит в себе тот же адрес,
// что и S1, но по смещению -8 от него уже будет записано число 2
Вот это и есть тот самый случай, когда копировние указателя приводит к изменению счетчика ссылок на данные, который хранится в самих данных.
Совершенно то же самое и с динамическими массивами.
var
A1, A2: array of Integer;
begin
SetLength(A1, 10);
// Теперь A1 указывает на байт в динамической памяти, с
// которого начинается тело массива. По смещению -8 от этого
// байта будет записано число 1.
A2 := A1;
// Копия тела массива не создается, а вместо этого происходит
// копирование адреса. Теперь A2 содержит в себе тот же адрес,
// что и A1, но по смещению -8 от него уже будет записано число 2
Это еще один случай, когда копировние указателя приводит к изменению счетчика ссылок на данные, который хранится в самих данных.
Все это легко и наглядно проверяется в окне CPU. Да и в книгах есть (и даже частично описано в справке).
Так вот, для случая массива нетипизированных указателей
array of pointer
компилятор никак не может узнать, что же именно записывается в элемент массива при его присваивании. А вдруг туда пишется адрес строки? или еще одного динамического массива? Тогда он должен увеличивать счетчик ссылок в этой строке или массиве. А вдруг нет? Тогда не должен. Вот и получается, что компилятор не может распознать, что же ему делать. Соответственно, он и не сможет построить гарантированно правильный код.
← →
Piter (2004-02-04 00:56) [39]>Вот это вообще новости! обязательно возьму на заметку, так все просто, что аж не по себе!
Вот вот! Я всегда говорил, что Сишники ругают Дельфи из-за того, что Дельфи то они и не знают!
P.S. А если честно - здесь столько нафлудили, что автор ветки, видимо, не вернется...
← →
Kris (2004-02-04 07:18) [40]
> Kris (03.02.04 23:46) [37]
> Удалено модератором
> Примечание: IMHO, уважаемый - это оффтоп...
Спасибо товаришь модератор! теперь вместо одного вопроса у меня появилось два. оказвыается я не знаю не ИМХО ни оффтоп. Спасибо еще раз помогли
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2004.02.17;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.008 c