Arrays
Динамические массивы
Очень простой пример...
Const MaxBooleans = (High(Cardinal) - $F) div sizeof(boolean); Type TBoolArray = array[1..MaxBooleans] of boolean; PBoolArray = ^TBoolArray; Var B : PBoolArray; N : integer; BEGIN N := 63579; {= получение памяти под динамический массив.. =} GetMem(B, N*sizeof(boolean)); {= работа с массивом... =} B^[3477] := FALSE; {= возвращение памяти в кучу =} {$IFDEF VER80} FreeMem(B, N*sizeof(boolean)); {$ELSE} FreeMem(B); {$ENDIF} END. |
Заполнения массива случаными неповторяющимися значениями Письмо от читателя...
Здравствуйте, Валентин. Огромное Вам спасибо за сбор и систематизацию советов по Delphi. Предлагаю Вам интересное решение заполнения массива случаными неповторяющимися значениями. Думаю этот алгоритм небесполезен.
type arr= array [1..255] of integer; procedure FillArray(var A: arr; n: integer); var i: integer; s: string; q: byte; begin randomize; s:=''; for i:=1 to n do begin q:=random(i); insert(chr(i),s, q); end; for i:=1 to n do begin A[i]:=ord(s[i]); end; end; |
e-mail: dedokv@mail.ru
He вступая в дискуссию, цитирую следующее письмо:
Заполнять ЦЕЛОЧИСЛЕННЫЙ массив, используя строки -- ЭТО ПОШЛО!
Приведу стандартную процедуру, работает в шесть раз быстрее, не имеет ограничений, да и кода поменьше :)
procedure FillArray(var A: array of Integer); var I, S, R: Integer; begin for I := 0 to High(A) do A[I] := I; for i := High(A) downto 0 do begin R := Random(I); S := A[R]; A[R] := A[I]; A[I] := S; end; end; |
e-mail: theodor_iv@mail.ru [000300]
Динамические массивы II В. Возможно создавать динамически-изменяющиеся массивы в Delphi?
О. Да. Для начала вам необходимо создать тип массива, использующего самый большой размер, который вам, вероятно, может понадобиться. В действительности, при создании типа никакой памяти не распределяется. Вот когда вы создаете переменную этого типа, тогда компилятор пытается распределить для вас необходимую память. Вместо этого создайте переменную, являющуюся указателем на этот тип. Этим вы заставите компилятор распределить лишь четыре байта, необходимые для размещения указателя.
Прежде, чем вы сможете пользоваться массивом, вам необходимо распределить для него память. Используя AllocMem, вы можете точно управлять выделяемым размером памяти. Для того, чтобы определить необходимое количество байт, которые вы должны распределить, просто умножьте размер массива на размер отдельного элемента массива. Имейте в виду, что самый большой блок, который вы сможете распределить в любой момент в 16-битной среде равен 64Kб. Самый большой блок, который вы можете в любой момент распределить в 32-битной среде равен 4Гб. Для определения максимального числа элементов, которые вы можете иметь в вашем конкретном массиве (в 16-битной среде), разделите 65,520 на размер отдельного элемента. Например: 65520 div SizeOf(LongInt)
Пример объявления типа массива и указателя:
type ElementType = LongInt; const MaxArraySize = (65520 div SizeOf(ElementType)); (* в 16-битной среде *) type MyArrayType = array[1..MaxArraySize] of ElementType; var P: ^MyArrayType; const ArraySizeIWant: Integer = 1500; |
procedure AllocateArray; begin if ArraySizeIWant <= MaxArraySize then P := AllocMem(ArraySizeIWant * SizeOf(LongInt)); end; |
Вот процедура, которая с помощью цикла устанавливает величину каждого члена:
procedure AssignValues; var I: Integer; begin for I := 1 to ArraySizeIWant do P^[I] := I; end; |
Помните также о том, что после использования массива всю распределенную память необходимо освободить. Вот пример того, как избавиться от этого массива:
procedure DeallocateArray; begin P := AllocMem(ArraySizeIWant * SizeOf(LongInt)); end; |
unit Unit1;
interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type ElementType = Integer; const MaxArraySize = (65520 div SizeOf(ElementType)); { в 16-битной среде } type { Создаем тип массива. Убедитесь в том, что вы установили максимальный диапазон, который вам, вероятно, может понадобиться. } TDynamicArray = array[1..MaxArraySize] of ElementType; TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; { Создаем переменную типа указатель на ваш тип массива. } P: ^TDynamicArray; const { Это типизированные константы. В действительности они являются статическими переменными, инициализирующимися во время выполнения указанными в исходном коде значениями. Это означает, что вы можете использовать типизированные константы точно также, как и любые другие переменные. Удобство заключается в автоматически инициализируемой величине. } DynamicArraySizeNeeded: Integer = 10; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin { Распределяем память для нашего массива. Будь внимательны и распределяйте размер, в точности необходимый для размещения нового массива. Если вы попытаетесь записать элемент, выходящий за допустимый диапазон, компилятор не ругнется, но объект исключения вам обеспечен. } DynamicArraySizeNeeded := 500; P := AllocMem(DynamicArraySizeNeeded * SizeOf(Integer)); { Как присвоить значение пятому элементу массива. } P^[5] := 68; end; procedure TForm1.Button1Click(Sender: TObject); begin { Вывод данных. } Button1.Caption := IntToStr(P^[5]); end; procedure TForm1.FormDestroy(Sender: TObject); begin { Освобождаем распределенную для массива память. } FreeMem(P, DynamicArraySizeNeeded * SizeOf(Integer)); end; end. |
Delphi 1
Пример массива констант (Array of Const) I " Array of const" это массив переменных, декларированных как константы. Непосредственно они представлены структурой TVarRec. Скобки просто ограничивают массив. Массив констант дает вам возможность передавать процедуре переменное количество параметров type-safe (безопасным) способом. Вот пример:
type
TVarRec = record
Data: record case Integer of
0: (L: LongInt);
1: (B: Boolean);
2: (C: Char);
3: (E: ^Extended);
4: (S: ^String);
5: (P: Pointer);
6: (X: PChar);
7: (O: TObject);
end;
Tag: Byte;
Stuff: array[0..2] of Byte;
end;
function PtrToStr(P: Pointer): String; const HexChar: array[0..15] of Char = '0123456789ABCDEF'; function HexByte(B: Byte): String; begin Result := HexChar[B shr 4] + HexChar[B and 15]; end; function HexWord(W: Word): String; begin Result := HexByte(Hi(W)) + HexByte(Lo(W)); end; begin Result := HexWord(HiWord(LongInt(P))) + ':' + HexWord(LoWord(LongInt(P))); end; procedure Display(X: array of const); var I: Integer; begin for I := 0 to High(X) do with TVarRec(X[I]), Data do begin case Tag of 0: ShowMessage('Integer: ' + IntToStr(L)); 1: if B then ShowMessage('Boolean: True') else ShowMessage('Boolean: False'); 2: ShowMessage('Char: ' + C); 3: ShowMessage('Float: ' + FloatToStr(E^)); 4: ShowMessage('String: ' + S^); 5: ShowMessage('Pointer: ' + PtrToStr(P)); 6: ShowMessage('PChar: ' + StrPas(X)); 7: ShowMessage('Object: ' + O.ClassName); end; end; end; procedure TForm1.Button1Click(Sender: TObject); var P: array[0..5] of Char; begin P := 'Привет'#0; Display([-12345678, True, 'A', 1.2345, 'ABC', Ptr($1234, $5678), P, Form1]); end; |
Delphi 1
Работа с большими массивами Распределите память кучи с помощью GetMem. Если вы имеете:
var a, b: array [0..30000]: Integer; |
type
TBigArray = array [0..30000] of Integer;
var a, b: ^TBigArray; |
GetMem(a, SizeOf(TBigArray)); GetMem(b, SizeOf(TBigArray)); |
a[0] := xxx; |
a^[0] := xxx; |
Использование многомерного массива
type RecType = integer; {<-- здесь задается тип элементов массива}
const MaxRecItem = 65520 div sizeof(RecType); type = MyArrayType = array[0..MaxRecItem] of RecType; type = MyArrayTypePtr = ^MyArrayType; var MyArray : MyArrayTypePtr; begin ItemCnt := 10; { количество элементов массива, которые необходимо распределить} GetMem(MyArray,ItemCnt*sizeof(MyArray[1])); {распределение массива} MyArray^[3] := 10; {доступ к массиву} FreeMem(MyArray,ItemCnt*sizeof(MyArray[1])); {освобождаем массив после работы с ним} end; |
Массив без ограничения типа и размера Пришло от читателя письмо:
Я тут посмотрел Ваши советы, и понял: это здорово! мне понравилось. Но в них я не нашел (может невнимательно смотрел?) возможности работать с массивами неограниченными по размеру и типу и вообще. Это работает начиная с Delphi 4
//к примеру опишем свой тип type MyType=record zap1:longword; zap2:char; zap3:string[10]; end; //опишем НЕОГРАНИЧЕННЫЙ массив переменный типа MyType //хотя, может использоваться абсолютно любой var m:array of MyType; .... procedure TForm1.Button1Click(Sender: TObject); var i:byte; begin for i:=0 to 9 do // нумерация элементов начинается с нуля! begin SetLength(m,Length(m)+1); // увеличение длины массива на 1 m[i].zap1:=i; // присвоение m[i].zap2:=chr(i); // полям m[i].zap3:=inttostr(i); // значений end; end; .... SetLength(m,0); // освобождение памяти end. |
Сергей Дьяченко, sd@arzamas.nnov.ru [000729]
Delphi 1
Динамические массивы III Вот "демо-модуль", демонстрирующий три различных способа ( далеко не все) создания динамических массивов. Все три способа для распределения достаточного количества памяти из кучи используют GetMem, tList используют для добавления элементов в список массива и используют tMemoryStream для того, чтобы распределить достаточно памяти из кучи и иметь к ней доступ, используя поток. Старый добрый GetMem вполне подходит для такой задачи при условии, что массив не слишком велик (<64K).
PS. Я не стал ловить в коде исключения (с помощью блоков Try...Finally}, которые могли бы мне помочь выявить ошибки, связанные с распределением памяти. В реальной системе вы должны быть уверены в своем грациозном владении низкоуровневыми операциями с памятью.
{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ Форма, демонстрирующая различные методы создания массива с }
{ динамически изменяемым размером. Разместите на форме четыре кнопки,}
{ компоненты ListBox и SpinEdit и создайте, как показано ниже, }
{ обработчики событий, возникающие при нажатии на кнопки. Button1, }
{ Button2 и Button3 демонстрируют вышеуказанных метода. Button4 }
{ очищает ListBox для следующего примера. }
{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
unit Dynarry1;
interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Spin; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; SpinEdit1: TSpinEdit; ListBox1: TListBox; Button4: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} Type pSomeType = ^SomeType; SomeType = Integer; procedure TForm1.Button1Click(Sender: TObject); Type pDynArray = ^tDynArray; tDynArray = Array[1..1000] Of SomeType; Var DynArray : pDynArray; I : Integer; begin { Распределяем память } GetMem ( DynArray, SizeOf(SomeType) * SpinEdit1.Value ); { Пишем данные в массив } For I := 1 to SpinEdit1.Value Do DynArray^[I] := I; { Читаем данные из массива } For I := SpinEdit1.Value DownTo 1 Do ListBox1.Items.Add ( 'Элемент ' + IntToStr(DynArray^[I]) ); { Освобождаем память } FreeMem ( DynArray, SizeOf(SomeType) * SpinEdit1.Value ); end; procedure TForm1.Button2Click(Sender: TObject); Var List : tList; Item : pSomeType; I : Integer; begin { Создаем список } List := tList.Create; { Пишем данные для списка } For I := 1 to SpinEdit1.Value Do Begin { Распределяем уникальный экземпляр данных } New ( Item ); Item^ := I; List.Add ( Item ); End; { Читаем данные из списка - базовый индекс списка 0, поэтому вычитаем из I единицу } For I := SpinEdit1.Value DownTo 1 Do ListBox1.Items.Add ( 'Элемент ' + IntToStr(pSomeType(List.Items[I-1])^) ); { Освобождаем лист } For I := 1 to SpinEdit1.Value Do Dispose ( List.Items[I-1] ); List.Free; end; procedure TForm1.Button3Click(Sender: TObject); Var Stream : tMemoryStream; Item : SomeType; I : Integer; begin { Распределяем память потока } Stream := tMemoryStream.Create; Stream.SetSize ( SpinEdit1.Value ); { Пишем данные в поток } For I := 1 to SpinEdit1.Value Do { Stream.Write автоматически отслеживает позицию записи, поэтому при записи данных за ней следить не нужно } Stream.Write ( I, SizeOf ( SomeType ) ); { Читаем данные из потока } For I := SpinEdit1.Value DownTo 1 Do Begin Stream.Seek ( (I-1) * SizeOf ( SomeType ), 0 ); Stream.Read ( Item, SizeOf ( SomeType ) ); ListBox1.Items.Add ( 'Элемент ' + IntToStr(Item) ); End; { Освобождаем поток } Stream.Free; end; procedure TForm1.Button4Click(Sender: TObject); begin ListBox1.Items.Clear; end; end. |
Delphi 1
Создание многомерного массива
type
PRow = ^TRow;
TRow = array[0..16379] of Single;
PMat = ^TMat; TMat = array[0..16379] of PRow; var Mat: PMat; X, Y, Xmax, Ymax: Integer; begin Write('Задайте размер массива: '); ReadLn(Xmax, Ymax); if (Xmax <= 0) or (Xmax > 16380) or (Ymax <= 0) or (Ymax > 16380) then begin WriteLn('Неверный диапазон. Не могу продолжить.'); Exit; end; GetMem(Mat, Xmax * SizeOf(PRow)); for X := 0 to Xmax - 1 do begin GetMem(Mat[X], Ymax * SizeOf(Single)); for Y := 0 to Ymax - 1 do Mat^[X]^[Y] := 0.0; end; WriteLn('Масси инициализирован и готов к работе.'); WriteLn(' Но эта программа закончила свою работу.'); end. |
Delphi 1
Запись массива на диск Скажем, ваша структура данных выглядит следующим образом:
type TMyRec = record SomeField: Integer; SomeOtherField: Double; TheRest: array[0..99] of Single; end; |
var Stream: TBlobStream; begin Stream := TBlobStream.Create(MyBlobField, bmWrite); Stream.Write(MyRec, SizeOf(MyRec)); Stream.Free; end; |
var Stream: TBlobStream; begin Stream := TBlobStream.Create(MyBlobField, bmRead); Stream.Read(MyRec, SizeOf(MyRec)); Stream.Free; end; |
Сохранение массива c изображениями Я решил проблему записи массива TBitmap в файл и его повторного чтения.
Идея заключается в загрузке каждого TBitmap во временный TMemoryStream. Член TMemoryStream.Size информирует о размере данных, которые нужно сохранить на диске. Затем мы пишем размер и сопровождаем его данными типа TFileStream. Эту манипуляцию мы проделываем для каждого TBitmap в массиве.
Для процедуры чтения сначала мы должны считать из потока размер данных TBitmap. Затем мы распределяем область для типа TMemoryStream полученного размера и считываем данные. Затем переписываем из TFileStream в TMemoryStream. И, наконец, мы читает из TMemoryStream сам TBitmap. Эту манипуляцию мы проделываем для каждого TBitmap в массиве.
Ниже я привел код, который я реально использовал. Код из игры Bingo, которую я разрабатываю, имеет сетку 5x5, чьи ячейки содержат изображение.
Реализация алгоритма весьма медленна, поэтому если вы имеете или найдете более быстрый алгоритм, пожалуйста, уведомите меня об этом. Если у вас есть любые вопросы, пожалуйста, свяжитесь со мной.
procedure TMainForm.SaveBoard;
var
MemoryStream: TMemoryStream;
FileStream : TFileStream;
Writer : TWriter;
Buffer : Pointer;
Size : Longint;
Column : Integer;
Row : Integer;
begin
MemoryStream := TMemoryStream.Create;
FileStream := TFileStream.Create (SaveFilename, fmCreate);
Writer := TWriter.Create (FileStream, $1000);
try
for Column := 0 to 4 do for Row := 0 to 4 do
begin
MemoryStream.Clear;
Bitmaps[Column, Row].SaveToStream (MemoryStream);
Buffer := MemoryStream.Memory;
Size := MemoryStream.Size;
Writer.WriteInteger (Size);
Writer.Write (Buffer^, Size);
end;
finally
Writer.Free;
FileStream.Free;
MemoryStream.Free;
end;
end;
procedure TMainForm.Open1Click(Sender: TObject); var MemoryStream: TMemoryStream; FileStream : TFileStream; Buffer : Pointer; Reader : TReader; Column : Integer; Row : Integer; Size : Longint; begin OpenDialog2.Filename := SaveFilename; if not OpenDialog2.Execute then Exit; MemoryStream := TMemoryStream.Create; FileStream := TFileStream.Create (OpenDialog2.Filename, fmOpenRead); Reader := TReader.Create (FileStream, $1000); try for Column := 0 to 4 do for Row := 0 to 4 do begin Size := Reader.ReadInteger; MemoryStream.SetSize (Size); Buffer := MemoryStream.Memory; Reader.Read (Buffer^, Size); Bitmaps[Column, Row].LoadFromStream (MemoryStream); end; finally Reader.Free; FileStream.Free; MemoryStream.Free; end; DrawGrid1.Repaint; SaveFilename := OpenDialog2.Filename; Caption := 'Bingo-создатель - ' + ExtractFilename (SaveFilename); end; |
Delphi 1
Пример массива констант (Array of Const) II Как использовать "array of const"?
Массив констант ( array of const) фактически является открытым массивом TVarRec (описание предекларированных типов Delphi вы можете найти в электронной справке). Приведенный ниже "псевдокод" на языке Object Pascal может послужить скелетом для дальнейшего развития:
procedure AddStuff( Const A: Array of Const );
Var i: Integer;
Begin
For i:= Low(A) to High(A) Do
With A[i] Do
Case VType of
vtExtended: Begin
{ добавляем натуральное число, все real-форматы
автоматически приводятся к extended }
End;
vtInteger: Begin
{ добавляем целое число, все integer-форматы автоматически приводятся к LongInt } End; vtObject: Begin If VObject Is DArray Then With DArray( VObject ) Do Begin { добавляем массив double-типа } End Else If VObject Is IArray Then With IArray( VObject ) Do Begin { добавляем массив integer-типа } End; End; End; { Case } End; { AddStuff } |
[000937]
Динамические массивы IV Виталий пишет:
Я хочу написать об использовании динамических массивов. Для того чтобы с ними работать можно использовать два вида объявлния в типах
type DAr = array of real; var A: DAr; |
var A:array of real; |
SetLength(A,7) |
I:=High(A); |
var a,b: array of integer; begin SetLength(a,2); SetLength(b,2); a[0]:=2; b[0]:=3; a:=b; b[0]:=4; end; |
Я надеюсь что это кому-нибудь поможет в работе.
Всего наилучшего. Виталий
P.S. Не советую изменять длину массивов в DLL, у меня при этом возникала ошибка Acess violation побороть ее мне так и не удалось. [000943]
Delphi 1
Массив констант во время выполнения приложения ...хорошо, непосредственно это синтаксис не поддерживает, поскольку массив констант Array of Const подобен открытым массивам, главным образом в части характеристик времени компиляции. Но вы можете обойти этот неприятный момент, обладая хотя бы начальными знаниями того, как реализован открытый массив. Что нам для этого необходимо: динамически размещенный массив array of TVarRec, который "принимает" ваши параметры, и "псевдоним" (alias) функции Format, позволяющий работать с таким массивом без "ругани" со стороны компилятора.
Type
{ объявляем тип для динамического массива array of TVarRecs }
TVarArray = Array [0..High(Word) div Sizeof(TVarRec)-1] of TVarRec;
PVarArray = ^TVarArray;
{ Объявляем alias-тип для функции Format. Передаваемые параметры будут иметь в стеке тот же самый порядок вызова, что и при нормальном вызове Format } FormatProxy = Function( Const aFormatStr: String; Var aVarRec: TVarRec; highIndex: Integer ): String; { AddVarRecs копирует параметры, передаваемые в массиве A в pRecs^, начиная с pRecs^[atIndex]. highIndex - самый большой доступный индекс pRecs, число распределенных элементов - 1. } Procedure AddVarRecs( pRecs: PVarArray; atIndex, highIndex: Integer; Const A: Array of Const ); Var i: Integer; Begin If pRecs <> Nil Then For i := 0 To High(A) Do Begin If atIndex <= highIndex Then Begin pRecs^[atIndex] := A[i]; Inc(atIndex); End { If } Else Break; End; { For } End; { AddVarRecs } procedure TScratchMain.SpeedButton2Click(Sender: TObject); Var p: PVarArray; S: String; Proxy: FormatProxy; begin { распределяем массив для четырех параметров, индексы - 0..3 } GetMem(p, 4*Sizeof(TVarRec)); try { добавляем параметры последующими вызовами AddVarRecs } AddVarRecs( p, 0, 3, [12, 0.5, 'Шаблон']); AddVarRecs( p, 3, 3, ['Тест'] ); { получаем полномочия Format } @Proxy := @SysUtils.Format; { Вызов с динамически сгенерированным массивом параметров. Естественно, строка формата может также быть сформирована и во время выполнения программы. } S := Proxy('Целое: %d, Реальное: %4.2f, Строки: %s, %s', p^[0], 3 ); { выводим результат } ShowMessage(S); finally FreeMem( p,4*Sizeof(TVarRec)); end; end; |
Тестировалось только в Delphi 1.0!
- Peter Below [000955]
Шаблон массива переменной длины Может ли кто мне подсказать как динамически создать массив записей и получить доступ к отдельным элементам?
Определите тип массива, которым может содержать максимальное количество записей, затем определите тип, являющийся указателем на массив. Идея заключается в том, чтобы не создавать экземпляр самого большого массива; а вместо этого использовать указательный тип и GetMem для распределения памяти для необходимого вам количества записей.
Я разработал то, что я называю шаблоном массива переменной длины "для бедных людей"...
unit %s; { ----------------------------------------------------------- ШАБЛОН МАССИВА ПЕРЕМЕННОЙ ДЛИНЫ Вы можете использовать этот шаблон для создания массива переменной длины любого типа данных. Для того, чтобы превратить шаблон с модуль, прогоните его через текстовый процессор, выполните во всем файле операцию поиска/замены для замены знака процента на ваш тип данных. ----------------------------------------------------------- } interface const %MaxCapacity = High( Cardinal ) div SizeOf( % ); type T%Index = 0..%MaxCapacity - 1; T%s = array[ T%Index ] of %; P%s = ^T%s; function %sSize( Capacity: T%Index ): Cardinal; function Get%s( Capacity: T%Index ): P%s; function Resize%s( var P: P%s; OldCapacity, NewCapacity: T%Index ): P%s; procedure Free%s( var P: P%s; Capacity: T%Index ); implementation uses SysUtils; function %sSize( Capacity: T%Index ): Cardinal; begin Result := Capacity * SizeOf( % ); end; function Get%s( Capacity: T%Index ): P%s; begin GetMem( Result, %sSize( Capacity )); end; function Resize%s( var P: P%s; OldCapacity, NewCapacity: T%Index ): P%s; begin ReAllocMem( P, %sSize( OldCapacity ), %sSize( NewCapacity )); end; procedure Free%s( var P: P%s; Capacity: T%Index ); begin FreeMem( P, %sSize( Capacity )); P := nil; end; end. |
Вот модуль, использующий после операции поиска и замены (см. выше) тип записи 'MyRecord', содержащий также определение этой записи. Поскольку "MyRecords" было очень длинным для имени модуля, я укоротил его. Имейте в виду, что PMyRecords - тип вашего переменного массива, если вы используете этот модуль.
unit MyRecs; interface type MyRecord = record AnInt: Integer; AString: string[ 10 ]; end; const MyRecordMaxCapacity = High( Cardinal ) div SizeOf( MyRecord ); type TMyRecordIndex = 0..MyRecordMaxCapacity - 1; TMyRecords = array[ TMyRecordIndex ] of MyRecord; PMyRecords = ^TMyRecords; function MyRecordsSize( Capacity: TMyRecordIndex ): Cardinal; function GetMyRecords( Capacity: TMyRecordIndex ): PMyRecords; function ResizeMyRecords( var P: PMyRecords; OldCapacity, NewCapacity: TMyRecordIndex ): PMyRecords; procedure FreeMyRecords( var P: PMyRecords; Capacity: TMyRecordIndex ); implementation uses SysUtils; function MyRecordsSize( Capacity: TMyRecordIndex ): Cardinal; begin Result := Capacity * SizeOf( MyRecord ); end; function GetMyRecords( Capacity: TMyRecordIndex ): PMyRecords; begin GetMem( Result, MyRecordsSize( Capacity )); end; function ResizeMyRecords( var P: PMyRecords; OldCapacity, NewCapacity: TMyRecordIndex ): PMyRecords; begin ReAllocMem( P, MyRecordsSize( OldCapacity ), MyRecordsSize( NewCapacity )); end; procedure FreeMyRecords( var P: PMyRecords; Capacity: TMyRecordIndex ); begin FreeMem( P, MyRecordsSize( Capacity )); P := nil; end; end. |
procedure TForm1.Button1Click( Sender: TObject ); var P: PMyRecords; begin P := GetMyRecords( 10 ); try P^[ 0 ].AnInt := 2001; P^[ 0 ].AString := 'Космическая одиссея'; finally FreeMyRecords( P, 10 ); end; end; |
Динамические массивы V SottNick пишет:
Если хочется, чтобы в многомерном массиве был разный размер у разных измерений например: VarArray: array[1..2, 1..?] of TType , где ? зависит от "строки" массива (1..2)
То дозволяется сделать так:
- Объявление
Var VarArray: array of array of array............ |
SetLength (VarArray, Razmernost1); // У первого измерения SetLength (VarArray[1], Razmernost2); // У второго измерения первой "строки" SetLength (VarArray[2], Razmernost3); // У второго измерения второй "строки" SetLength (VarArray[n], Razmernost4); // У второго измерения n-ной "строки" SetLength (VarArray[1][1], Razmernost5); // У третьего измерения первой "строки" первого "столбца" SetLength (VarArray[1][2], Razmernost6); // У третьего измерения первой "строки" второго "столбца" SetLength (VarArray[n][m], Razmernost7); // У третьего измерения n-ной "строки" m-ного "столбца" |
Все можно изменять в процессе естественно.
Razmernost1:=Length (VarArray); // У первого измерения (количество строк) Razmernost2:=Length (VarArray[1]); // У второго измерения первой "строки" (количество столбцов) Razmernost3:=Length (VarArray[2]); // У второго измерения второй "строки" (количество столбцов) Razmernost4:=Length (VarArray[n]); // У второго измерения n-ной "строки" (количество столбцов) Razmernost5:=Length (VarArray[1][1]); // У третьего измерения первой "строки" первого "столбца" Razmernost6:=Length (VarArray[1][2]); // У третьего измерения первой "строки" второго "столбца" Razmernost7:=Length (VarArray[n][m]); // У третьего измерения n-ной "строки" m-ного "столбца" |
SetLength (VarArray, 0); // Всех сразу |
Delphi 1
Пример массива констант (Array of Const) III
procedure foo(a : array of const); implementation var var1 : longint; var2 : pointer; var3 : integer; begin var1 := 12345678; var2 := @var1; var3 := 1234; foo([var1, var2, var3]); |
Определите тип, например, так:
TYPE NAME1 = Array[1..4,1..10] of Integer; |
NAME2 : NAME1 = ((1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10), (1,2,3,4,5,6,7,8,9,10)); |
Delphi 1
Массив объектов-изображений Вы не сможете сделать это напрямую и "визуально", но если вы не возражаете против нескольких строк кода, то я покажу как это может быть просто:
type TForm1 = class(TForm) ... public images: array [1..10] of TImage; ... end; procedure TForm1.FormCreate(...); var i: integer; begin ... for i := 1 to 10 do begin images[i] := TImage.Create(self); with images[i] do begin parent := self; tag := i; { это облегчит идентификацию изображения } ... установите другие необходимые свойства, например: OnClick := MyClickEventHndlr; end; end; ... end; |
Delphi 1
Массив TPOINT
Const ptarr : Array[0..4] Of TPoint = ((x:0; y:4), . . (x:4; y:4)); |
Delphi 1
Динамические массивы VI Например, если вам необходимо сохранить "GIZMOS" в вашем массиве, сделайте следующее:
CONST MaxGIZMOS = $FFFF Div (SizeOf(GIZMOS)) { или что- то другое, смотря какой максимальный размер GIZMOS вы планируете...} TYPE pGIZMOArray = ^GIZMOArray; GIZMOArray = Array[1..MaxGIZMOS] of GIZMOS; VAR TheGIZMOS: pGIZMOArray; GIZMOcount: integer; BEGIN GetMem(TheGIZMOS,(GIZMOcount+1)*SizeOf(GIZMO)); {Нужна дополнительная единица, поскольку массив GetMem ведет отсчет с нуля...} TheGIZMOS^[index] := Whatever; ну и так далее... |
Delphi 1
Создание больших массивов В 16- битной версии Delphi нельзя сделать это непосредственно. В новой, 32-битной версии, это как-то можно сделать, но за два месяца колупания я так и не понял как. (Некоторые бета-тестеры знают как. Не могли бы они сообщить нам всю подноготную этого дела?)
В 16-битной версии Delphi вам необходимо работать с блоками по 32K или 64K и картой. Вы могли бы сделать приблизительно следующее:
type chunk: array[0..32767] of byte; pchunk: ^chunk; var BigArray: array[0..31] of pChunk; |
for i := 0 to high(bigArray) do new (bigArray[i]); |
bigArray[n shr 15]^[n and $7FFF] := y; x := bigArray[n shr 15]^[n and $7fff]; |
n должен находиться в диапазоне [0..32*32*1024] = [0..1024*1024] = [0..1048576].
Для освобождения массива после его использования необходимо сделать следующее:
for i := 0 to high(bigArray) do dispose (bigArray[i]); |
Delphi 1
Динамические массивы VII Существует несколько способов сделать это. Применять тот или иной способ зависит от того, какой массив вы используете - массив строк или массив чисел (целые, натуральные и пр.).
- Если вам необходим простой динамический одномерный массив строк, я предлагаю вам взглянуть на компонент tStringList, он сам заботится о функциях управления и легок в использовании.
Допустим у вас есть трехмерный массив строк, текущее измерение [12,80,7], и вы хотите найти элемент [n,m,x]. Вы можете найти этот элемент в приведенном одномерном массиве с помощью формулы ((n-1)*80*7 + (m-1)*80 + x). Затем вы можете использовать это в качестве индекса в tStringList. Для диманического изменения одной из границ массива, используйте метод tStringList Move, служащий как раз для таких целей. (Метод состоит из некоторых технологических внутренних циклов, но выполняются они очень быстро, поскольку tStringList манипулирует не с самими строками, а с указателями на них.)
type bigArray: array[1..32000] of integer; {или ^double, или что-то еще} pMyArray: ^bigArray; |
getMem (pMyArray, sizeof(integer) * n); |
pMyArray^[51] |
Изменить размер массива, определить новый указатель, перераспределить или обменяться с другим массивом можно так:
pTemp: ^bigArray;
getMem (pTemp, sizeof(integer) * newnumelements); memcopy (pTemp, pMyArray, sizeof(integer)*n); {n - количество элементов в pMyArray} freeMem (pMyArray, sizeof(integer)*n); pMyArray := pTemp; |
Лично я инкапсулировал все в своем объекте. Я использую, как я это называю, "Basic String Object" (BSO), базовый строковый объект, который осуществляет динамическое распределение и освобождение памяти для строк любого размера. Непосредственно это PChar, указывающий на распределенную память. У меня существует два внешних свойства: AsString и AsPChar. Также у меня есть различные свойства и методы, позволяющие иметь различные способы доступа и манипулировать строками.
Я написал свои собственные malloc(), calloc() и realloc(), используя частные методы объекта TString для сканирования распределенной памяти. Это классно работает, когда мне нужно "захватить" блок памяти.
С помощью двух методов я могу распределить необходимую мне память (блоками, так что это не занимает много процессорного времени), и освобождать ее (когда существует определенный резерв -- и снова так, чтобы не тратить много процессорного времени).
О другой идее я уже рассказывал (открытый массив). Если вам нужна проверка выхода за границы и/или динамическое изменение размера массива, вы можете использовать метод, аналогичный методу работы со строковым объектом (описанный мною выше), но вам необходимо будет интегрировать свойство-массив по умолчанию, чтобы иметь к нему простой доступ. Это позволит вам иметь индексы и использовать нужный вам тип.
TMyDynamicObject = ... PROPERTY Array[ idx :LONGINT ]:TMyType READ GetArray WRITE PutArray DEFAULT; ... VAR Mine :TMyDynamicObject; ... Mine := TMyDynamicObject.Create; FOR i := 10 TO 20 DO Mine[i] := {значение} {ЧУДОВИЩНАЯ РАСТРАТА ПАМЯТИ - если вы действительно используете такие большие массивы и хэш-таблицы } Mine[-100000] := {значение} Mine[+100000] := {значение} |
Для того, чтобы хранить все, что вы хотите, вы можете использовать TList (или TStringList.Objects)! TList.Items хранят указатели на объекты или записи, но они ничего не могут сделать с ними, поэтому вы можете привести их к типу longint, и больше о них не беспокоиться! Вот пример хранения в TList списка целых:
var aList: TList; I : Integer; L : Longint; begin aList := TList.Create; L := 93823; aList.Add(Pointer(L)); aList.Add(Pointer(83293)); for I := 1 to aList.Count do L := L + Longint(aList.Items[I-1]); aList.Free; end; |
type PMyRec = TMyRec; TMyRec = record Name: string[40]; Addr : string[25]; Comments: string; salary: Double; end; var aList: TList; aRecPtr: PMyRec; I : Integer; begin aList := TList.Create; New(aRecPtr); with aRecPtr^ do begin Name := 'Валентин'; Addr := 'неизвестен'; Comments := 'Автор Советов по Delphi'; Salary := 999000.00; end; aList.Add(aRecPtr); aList.Add(... ); ... for I := 1 to aList.Count do begin aRecPtr := PMyRec(aList.Items[I-1]); {что-то делаем с записью} end; {теперь избавляемся от всех записей и самого списка-объекта} for I := 1 to aList.Count do Dispose(PMyRec(aList.Items[I-1])); aList.Free; end; |
Динамические массивы VIII Иногда разработчик, работая с массивами, не знает какого размера массив ему нужен. Тогда Вам пригодится использование динамических массивов.
var intArray : array of integer; |
begin intArray:=(New(IntArray,100); //Размер массива? 100 end; |
Delphi 1
Массивы размером более 64К Не существует способа непосредственного доступа к массиву размером свыше 65520. Или вы пользуетесь для распределения памяти GlobalAlloc или TMemoryStream, и создаете специализированный класс для доступа к элементам массива, или вы делаете это непосредственно вручную. Добраться до следующих сегментов GlobalAlloc-ого объекта можно, строя указатели с помощью SelectorInc. Самый легкий путь заключается в использовании TMemoryStream.
Type Tmyarr = Class buffer : TMemoryStream; elsize : LongInt; Constructor Create(esize, number : Word); Destructor Free; Procedure SetElement(index : Word; p : Pointer); Procedure GetElement(index : Word; p : Pointer); End; Implementation Constructor Tmyarr.Create(esize, number : Word); Var size : LongInt; Begin Inherited Create; buffer := TMemoryStream.Create; elsize := esize; size := esize * number; buffer.SetSize(size); End; Procedure Tmyarr.Free; Begin If Self <> Nil Then Begin buffer.Free; Destroy; End; End; Procedure GetElement(index : Word; p : Pointer); Begin buffer.Seek(elsize * index, 0); buffer.Read(p^, elsize); End; Procedure SetElement(index : Word; ptr : Pointer); Begin buffer.Seek(elsize * index, 0); buffer.Write(p^, elsize); End; |
Delphi 1
Массивы элементов управления Вы без проблем сможете иметь массив объектов, но он не будет создаваться для вас автоматически визуальным дизайнером. Ограничений здесь никаких нет. Во всех случаях, где я его использовал, у меня было более одного элемента управления (в одной реальной задаче у меня получался динамический массив из 26 компонентов Image, каждый из которых имел изображение, свой размер, текст всплывающей подсказки, и обрамление) и их координат, которые обычно более сложны в описании, чем вектора (в моем примере они располагались в двумерном массиве, который зависел от текущего размера области окна клиента и имел "обратную связь" с такими параметрами, как размер окна, набор аппаратных устройств и каталогов общего доступа).
Мое предложение заключается в создании единственного прототипа в виде сложного элемента управления, который располагается на форме, невидим, и не включен (enabled). Для этого необходимо определить наследника TObject, который может представлять содержащиеся в массиве компоненты, этот шаг можно опустить, если структура компонента имеет собственный иерархический объект верхнего уровня подобно TPanel). Наконец, определите массив для этого компонента, и поместите на форму переменную такого типа.
Во время выполнения приложения для добавления элементов массива вам необходимо будет создавать компоненты (используйте прототип для инициализации большинства свойств) и устанавливать атрибуты для их выравнивания относительно формы. Не забывайте для динамически создаваемых компонентов задавать родителя, в противном случае они просто не появятся на форме (владелец прототипа для включения новых элементов управления). Это может показаться довольно сложным на слух, но в реальности все просто и требует очень небольшого кодирования. Данный способ предоставляет больше возможностей, чем это может сделать массив VB -- в таких ключевых моментах и кроется разница между языками типа ObjectPascal и RAP-инструментами типа VB. [001990]