Dynamic array/ru
│
English (en) │
español (es) │
suomi (fi) │
français (fr) │
日本語 (ja) │
русский (ru) │
Динамический массив - это массив, размеры которого неизвестны во время компиляции. Динамический массив не является единственным типом, предоставляющим массивы переменной длины, но с 2018 года он является единственным, который поддерживает FPC.
Использование
Концепция
Определение динамического массива будет выделять пространство только для указателя. Во время выполнения различные подпрограммы будут обеспечивать удобное использование, но что более важно, синтаксис доступа к элементам массива путем помещения индексов в квадратные скобки поддерживается компилятором (реализован как автоматическое разыменование указателя).
Индексы динамических массивов всегда являются неотрицательными целыми числами, начинающимися с нуля для первого элемента.
Невозможно использовать перечислительный тип, любой другой порядковый тип в качестве индекса или изменить первый элемент, заданный индексом 1
.
Определение
Одномерный динамический массив определяется так:
array of char
Обратите внимание, что размерность [массива] не указан.
Для определения многомерного массива в качестве базового типа указывается сам массив.
array of array of longInt
Задание размерности
Процедура компилятора setLength
изменит длину динамического массива при условии, что памяти достаточно.
1program setLengthDemo(input, output, stdErr);
2var
3 sieve: array of longWord;
4begin
5 setLength(sieve, 1337);
6end.
Процедура выделяет память для стольких записей базового типа, сколько указано, плюс некоторые данные для управления. Затем она copies (копирует) все элементы старого массива в новый. Новые поля, которых раньше не было, инициализируются по умолчанию с помощью встроенного механизма.
Размер многомерных массивов также можно изменить с помощью setLength
.
1program multidimensionalSetLengthDemo(input, output, stdErr);
2var
3 samples: array of array of smallInt;
4begin
5 setLength(samples, 12, 64);
6end.
Допустимые индексы для первого измерения выборки находятся в диапазоне 0..11
, в то время как действительные индексы для его второго измерения находятся в диапазоне 0..63
.
Один весьма полезный факт заключается в том, что ограничение в неизменности всех размеров [статического массива] не распространяется на динамические массивы.
1program binomialPotence(input, output, stdErr);
2var
3 pascalsTriangle: array of array of longWord;
4 exponent: longInt;
5 factor: longInt;
6begin
7 setLength(pascalsTriangle, 20);
8
9 setLength(pascalsTriangle[0], 1);
10 pascalsTriangle[0][0] := 1;
11
12 setLength(pascalsTriangle[1], 2);
13 pascalsTriangle[1][0] := 1;
14 pascalsTriangle[1][1] := 1;
15
16 // строим значения простым сложением
17 for exponent := 2 to high(pascalsTriangle) do
18 begin
19 setLength(pascalsTriangle[exponent], exponent + 1);
20 pascalsTriangle[exponent][0] := 1;
21 pascalsTriangle[exponent][exponent] := 1;
22 for factor := 1 to exponent - 1 do
23 begin
24 pascalsTriangle[exponent][factor] :=
25 pascalsTriangle[exponent - 1][factor - 1] +
26 pascalsTriangle[exponent - 1][factor];
27 end;
28 end;
29
30 // ...
Инициализация
Начиная с FPC 3.0.0 типы динамических массивов, которые не являются анонимными, автоматически снабжаются «конструктором», как это возможно уже знакомо по объектно-ориентированному программированию. Это позволяет объединить вызовы setLength и серию присвоения значений в одном операторе:
1program dynamicArrayCreateDemo(input, output, stdErr);
2type
3 truths = array of boolean;
4var
5 l: truths;
6begin
7 l := truths.create(false, true, true, false, true, false, false);
8end.
Конечно, вы также можете вкладывать массивы:
1program nestedDynamicArrayCreateDemo(input, output, stdErr);
2type
3 truths = array of boolean;
4 pattern = array of truths;
5var
6 p: pattern;
7begin
8 p := pattern.create(
9 truths.create(false, false),
10 truths.create(true, false),
11 truths.create(true, false, false),
12 truths.create(true, true, false)
13 );
14end.
handling
Keep in mind dynamic arrays are pointers. Assigning dynamic array variables to each other does not copy any payload, but just the address. This differs from static arrays' behavior.
If you want to duplicate data you have to use system.copy
.
1program dynamicArrayCopyDemo(input, output, stdErr);
2
3var
4 foo, bar: array of char;
5
6procedure printArrays;
7begin
8 writeLn('foo[0] = ', foo[0], '; bar[0] = ', bar[0]);
9end;
10
11begin
12 setLength(foo, 1);
13 foo[0] := 'X';
14 // copies _reference_
15 bar := foo;
16 write(' initial values: ');
17 printArrays;
18
19 // change content _via_ _second_ reference
20 bar[0] := 'O';
21 write('changed via 2nd ref: ');
22 printArrays;
23
24 // copy content
25 bar := copy(foo, 0, length(foo));
26 bar[0] := 'X';
27 write(' copied and changed: ');
28 printArrays;
29end.
Only by using copy
both arrays can be modified independently.
As stated above, setLength
copies data.
The highlighted line in the example above is (semantically) equivalent to setLength(bar, length(bar))
.
Dynamic arrays are reference counted.
Calling setLength(myDynamicArrayVariable, 0)
virtually does myDynamicArrayVariable := nil
and decreases the reference count.
Only when the reference count hits zero, the memory block is released.
1program dynamicArrayNilDemo(input, output, stdErr);
2var
3 foo, bar: array of char;
4begin
5 setLength(foo, 1);
6 foo[0] := 'X';
7 // copy _reference_, increase reference count
8 bar := foo;
9 // foo becomes nil, reference count is decreased
10 setLength(foo, 0);
11 writeLn('length(foo) = ', length(foo),
12 '; length(bar) = ', length(bar));
13
14 // decrease reference count another time
15 bar := nil;
16 writeLn('length(foo) = ', length(foo),
17 '; length(bar) = ', length(bar));
18end.
Nonetheless, dynamic arrays are finalized automatically.
It is not necessary to manually setLength(…, 0)
on all your references when the program comes to end, or when leaving a scope in general.
Without {$rangeChecks on}
it is possible to reach beyond an array's limits.
That means when iterating over dynamic arrays, it is impossible to work without low
and high
to determine valid indices (the former being optional, since dynamic arrays always start at zero).
Alternatively, for … in
loops can be used, if no index is required.
Remember, sizeOf
of a dynamic array evaluates to the size of a pointer.
application
- there is
system.tBoundArray
andobjPas.tBoundArray