Советы по Delphi

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.
[000026]


Заполнения массива случаными неповторяющимися значениями Письмо от читателя...

Здравствуйте, Валентин. Огромное Вам спасибо за сбор и систематизацию советов по 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;
Не забывайте о том, что величина ArraySizeIWant должна быть меньше или равна MaxArraySize.

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

    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.
[000362]



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;
-Steve [000544]



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;
[000549]



Использование многомерного массива
    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;
- Michael Day [000724]



Массив без ограничения типа и размера Пришло от читателя письмо:

Я тут посмотрел Ваши советы, и понял: это здорово! мне понравилось. Но в них я не нашел (может невнимательно смотрел?) возможности работать с массивами неограниченными по размеру и типу и вообще. Это работает начиная с 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.
C Уважением,
Сергей Дьяченко, 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.
- Robert Wittig [000759]



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.
- Steve Schafer [000764]



Delphi 1

Запись массива на диск Скажем, ваша структура данных выглядит следующим образом:

    type TMyRec = record SomeField: Integer; SomeOtherField: Double; TheRest: array[0..99] of Single; end;
и TBlobField имеет имя MyBlobField. TMyRec назван как MyRec. Для копирования содержимого MyRec в MyBlobField необходимо сделать следующее:

    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;
- Steve Schafer [000787]



Сохранение массива 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;
Опубликовал Michael Vincze (102224.3340.compuserve.com) [000828]



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 }
Для получения дополнительной информации загляните в главу "open arrays" электронной справки.

[000937]



Динамические массивы IV Виталий пишет:

Я хочу написать об использовании динамических массивов. Для того чтобы с ними работать можно использовать два вида объявлния в типах

    type
DAr = array of real; var
A: DAr;
или сразу

    var A:array of real;
Таким образом мы объяыили ссылку на область памяти. Для того чтобы указать размер воспользуемся процедурой SetLength, ее можно использовать в любых местах и определять размера массива тот, который необходим в данную минуту.

    SetLength(A,7)
Так мы создали массив состоящий из 7 элементов начиная с 0. Важно! Первый элемент в динамическом массиве всегда нулевой. Для определения верхний границы используем функцию Hihg

    I:=High(A);
I - верхняя граница. Для определения длины Length(A), для определения нижней границы Low(A). При нулевой длине массива High, возращает -1. Пример:

    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;
После этих манипуляций а[0] равно 4. Дело в том при присвоении a:=b не происходит копирование т.к. а, b, это всего лишь указатели на область памяти. Для копирования необходимо использовать функцию Copy.

Я надеюсь что это кому-нибудь поможет в работе.

Всего наилучшего. Виталий

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;
Я надеюсь вы поняли принцип. Естественно, имеются ограничения. Вы можете передавать в AddVarRecs числовые величины, строковые переменные и литералы, но не в коем случае не строковые выражения! В этом случае компилятор должен для хранения результата сформировать в стеке временную строку, передать ее в AddVarRecs (или лучше по адресу в TVarRec), и она может прекратить свое существование или может быть перезаписана в стеке другими данными, если в конечном счете вы передадите в Proxy целый массив!

Тестировалось только в 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;
- Ed Jordan [000990]



Динамические массивы V SottNick пишет:

Если хочется, чтобы в многомерном массиве был разный размер у разных измерений например: VarArray: array[1..2, 1..?] of TType , где ? зависит от "строки" массива (1..2)

То дозволяется сделать так:
  1. Объявление


    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-ного "столбца"
  • Обращение VarArray[n][m][o][p][r]:=1; // :Integer // К элементу n-ной "строки", m-ного "столбца", // o-того "?", p-того "?", r-того "?"
  • Обнуление (освобождение памяти)


  •     SetLength (VarArray, 0); // Всех сразу
    [001043]



    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]);
    В действительности, массив array of const более корректным было бы назвать массивом array of tvariant. Tvariant - множественный выбор типов переменной, в которой можно задать номер типа. В Visual Basic у него имеется наследник. Delphi также позволяет использовать имена.

    Определите тип, например, так:

        TYPE
    NAME1 = Array[1..4,1..10] of Integer;
    Затем, в вашей секции CONST:

        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));
    [001171]



    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;
    Для того, чтобы убедиться, что все модули в секции "uses" установлены правильно, бросьте на форму один такой динамический компонент, и затем удалите его, или установите его видимость в False. Более сложный способ заключается в разработке собственного компонента, делающего описанное выше. [001173]



    Delphi 1

    Массив TPOINT
        Const
    ptarr : Array[0..4] Of TPoint = ((x:0; y:4), . . (x:4; y:4));
    [001175]



    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; ну и так далее...
    TList - такой динамический массив. Для получения дополнительной информации обратитесь к электронной справке. Если вы хотите это делать сами, то вам необходимо использовать GetMem для получения указателя на распределенную динамическую память, и затем FreeMem для освобождения памяти, занятой динамическим массивом. Tlist сделает это за вас самым надежным образом. [001180]



    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]);
    Для получения доступа к n-ному байту в пределах массива (n должен иметь тип longint):

        bigArray[n shr 15]^[n and $7FFF] := y; x := bigArray[n shr 15]^[n and $7fff];
    Это даже осуществляет проверку выхода за границы диапазона, если вы установили в ваших настройках опцию "range checking"!

    n должен находиться в диапазоне [0..32*32*1024] = [0..1024*1024] = [0..1048576].

    Для освобождения массива после его использования необходимо сделать следующее:

        for i := 0 to high(bigArray) do dispose (bigArray[i]);
    [001182]



    Delphi 1

    Динамические массивы VII Существует несколько способов сделать это. Применять тот или иной способ зависит от того, какой массив вы используете - массив строк или массив чисел (целые, натуральные и пр.).
    1. Если вам необходим простой динамический одномерный массив строк, я предлагаю вам взглянуть на компонент tStringList, он сам заботится о функциях управления и легок в использовании.


  • Если вам необходим динамический многомерный массив строк, вы также можете воспользоваться tStringList (в случае, если число элементов вашего массива не превышает лимит для tStringList, я полагаю он равен 16,000). Чтобы сделать это, создайте функцию линейного распределения как показано ниже:


  • Допустим у вас есть трехмерный массив строк, текущее измерение [12,80,7], и вы хотите найти элемент [n,m,x]. Вы можете найти этот элемент в приведенном одномерном массиве с помощью формулы ((n-1)*80*7 + (m-1)*80 + x). Затем вы можете использовать это в качестве индекса в tStringList. Для диманического изменения одной из границ массива, используйте метод tStringList Move, служащий как раз для таких целей. (Метод состоит из некоторых технологических внутренних циклов, но выполняются они очень быстро, поскольку tStringList манипулирует не с самими строками, а с указателями на них.)

  • Если вам необходим динамический одномерный массив чисел, то в общих словах я приведу его ниже, но есть масса мелких деталей. Объявите указатель на тип массива, имеющего максимальное количество элементов данного типа (помните о том, что Delphi-16 позволяет иметь типам область памяти, ограниченной 64K), например так:


  •     type bigArray: array[1..32000] of integer;  {или ^double, или что-то еще} pMyArray: ^bigArray;
    затем распределите сам массив:

        getMem (pMyArray, sizeof(integer) * n);
    , где n - количество элементов. После этого вы можете ссылаться на элементы массива следующим образом:

        pMyArray^[51]
    Не забудьте освободить массив с помощью FreeMem после того, как вы его использовали.

    Изменить размер массива, определить новый указатель, перераспределить или обменяться с другим массивом можно так:

        pTemp: ^bigArray;
    getMem (pTemp, sizeof(integer) * newnumelements); memcopy (pTemp, pMyArray, sizeof(integer)*n); {n - количество элементов в pMyArray} freeMem (pMyArray, sizeof(integer)*n); pMyArray := pTemp;
  • Если вам необходим многомерный массив чисел, скомбинируйте технику, описанную в пункте (3), с функцией распределения, описанной в пункте (2).


  • Если для вашего массива необходим участок памяти больше чем 64K, вам необходимо разработать список указателей на участки памяти, но эта тема выходит за рамки данной статьи.


  • Лично я инкапсулировал все в своем объекте. Я использую, как я это называю, "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] := {значение}
    Если в вашем распоряжении находится "редкозаполненный" массив, использование хэш-таблицы дало бы существенный выигрыш. Я преобразую индексные значения в строки, а все остальное перепоручаю TStrings, но не из-за того, что я такой ленивый, а из-за того, что он сделает это лучше меня, мне нужно всего лишь осуществить преобразование в строки.

    Для того, чтобы хранить все, что вы хотите, вы можете использовать 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;
    В TList и TStringList вы можете иметь до 16380 элементов. А теперь обещанный пример того, как можно хранить в TList записи (или объекты), вернее, указатели на них:

        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;
    [001200]



    Динамические массивы VIII Иногда разработчик, работая с массивами, не знает какого размера массив ему нужен. Тогда Вам пригодится использование динамических массивов.

        var
    intArray : array of integer;
    При таком объявлении размер массива не указывается. Что бы использовать его дальше необходимо определить его размер (обратите внимание, что размер динамического массива можно устанавливать в программе):

        begin
    intArray:=(New(IntArray,100); //Размер массива? 100
    end;
    Igor Nikolaev aKa The Sprite [001416]



    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;
    [001976]



    Delphi 1

    Массивы элементов управления Вы без проблем сможете иметь массив объектов, но он не будет создаваться для вас автоматически визуальным дизайнером. Ограничений здесь никаких нет. Во всех случаях, где я его использовал, у меня было более одного элемента управления (в одной реальной задаче у меня получался динамический массив из 26 компонентов Image, каждый из которых имел изображение, свой размер, текст всплывающей подсказки, и обрамление) и их координат, которые обычно более сложны в описании, чем вектора (в моем примере они располагались в двумерном массиве, который зависел от текущего размера области окна клиента и имел "обратную связь" с такими параметрами, как размер окна, набор аппаратных устройств и каталогов общего доступа).

    Мое предложение заключается в создании единственного прототипа в виде сложного элемента управления, который располагается на форме, невидим, и не включен (enabled). Для этого необходимо определить наследника TObject, который может представлять содержащиеся в массиве компоненты, этот шаг можно опустить, если структура компонента имеет собственный иерархический объект верхнего уровня подобно TPanel). Наконец, определите массив для этого компонента, и поместите на форму переменную такого типа.

    Во время выполнения приложения для добавления элементов массива вам необходимо будет создавать компоненты (используйте прототип для инициализации большинства свойств) и устанавливать атрибуты для их выравнивания относительно формы. Не забывайте для динамически создаваемых компонентов задавать родителя, в противном случае они просто не появятся на форме (владелец прототипа для включения новых элементов управления). Это может показаться довольно сложным на слух, но в реальности все просто и требует очень небольшого кодирования. Данный способ предоставляет больше возможностей, чем это может сделать массив VB -- в таких ключевых моментах и кроется разница между языками типа ObjectPascal и RAP-инструментами типа VB. [001990]


    Содержание раздела