Interested Article - Массив переменной длины
- 2021-02-17
- 1
В программировании массив переменной длины ( англ. variable-length array, VLA, variable-sized array, runtime-sized array ) представляет собой массив , длина которого определяется во время выполнения (а не во время компиляции) . В C массив переменной длины имеет управляемый переменной тип ( англ. variably modified type ), который зависит от какого-либо значения (см. Зависимый тип ).
Основная цель массивов переменной длины — это упростить программирование численных алгоритмов.
Языки программирования, поддерживающие массивы переменной длины:
Ada
,
Algol 68
(без возможности менять длину строк в двумерных массивах и т. д.),
APL
,
C99
(хотя впоследствии массив переменной длины стал в
C11
необязательной возможностью, поддержка которой не требуется
; на некоторых платформах это могло быть реализовано ранее с помощью функции
()
или аналогичных ей) и
C#
(массивы, выделенные на стеке — эта возможность доступна только в небезопасном режиме),
COBOL
,
Fortran 90
,
J
и
Object Pascal
(язык, используемый в средах
Borland Delphi
и
Lazarus
, компилирующийся с помощью Free Pascal Compiler).
Память
Выделение памяти
- GNU C Compiler выделяет память для массива переменной длины с автоматическим сроком хранения ( англ. automatic storage duration ) на . Это более быстрый и простой вариант по сравнению с выделением в куче, и он используется большинством компиляторов.
- Массивы переменной длины также могут быть выделены в куче, внутри реализации используется указатель на этот блок.
Реализация
C99
Следующая функция на
C99
выделяет массив переменной длины заданного размера, заполняет его значениями с плавающей запятой, а затем передаёт его другой функции для обработки. Поскольку массив объявлен как автоматическая переменная, его время жизни заканчивается, когда возвращается
read_and_process()
.
float read_and_process(int n)
{
float vals[n];
for (int i = 0; i < n; ++i)
vals[i] = read_val();
return process(n, vals);
}
В C99 параметр длины должен предшествовать параметру массива переменной длины при вызовах функций
. В C11 определяется макрос
__STDC_NO_VLA__
, если массивы переменной длины не поддерживаются
. GCC имел массивы переменной длины в качестве расширения до C99, которое также распространяется на его диалект C++.
Линус Торвальдс в прошлом выражал своё недовольство использованием массивов переменной длины малых размеров, поскольку это порождает ассемблерный код более низкого качества . Ядро Linux 4.20 фактически не содержит массивов переменной длины .
Хотя C11 явно не указывает ограничение по размеру для массивов переменной длины, некоторые интерпретации полагают, что они должны иметь тот же максимальный размер, что и все другие объекты, т.е.
SIZE_MAX
байт
. Однако такую интерпретацию следует понимать в более широком контексте ограничений среды и платформы, таких как типичный размер страницы с защитой стека 4 КиБ, что на много порядков меньше, чем
SIZE_MAX
.
Можно воспользоваться синтаксисом, подобным массиву переменной длины, с динамическим хранением с помощью указателя на массив.
float read_and_process(int n)
{
float (*vals)[n] = malloc(sizeof(float[n]));
for (int i = 0; i < n; ++i)
(*vals)[i] = read_val();
float ret = process(n, *vals);
free(vals);
return ret;
}
Ada
Ниже приведён тот же пример на языке Ada . Массивы содержат свою длину вместе с данными, поэтому нет необходимости передавать их длину функции Process.
type Vals_Type is array (Positive range <>) of Float;
function Read_And_Process (N : Integer) return Float is
Vals : Vals_Type (1 .. N);
begin
for I in 1 .. N loop
Vals (I) := Read_Val;
end loop;
return Process (Vals);
end Read_And_Process;
Fortran 90
Эквивалентная функция на языке Fortran 90 .
function read_and_process(n) result(o)
integer,intent(in)::n
real::o
real,dimension(n)::vals
integer::i
do i = 1,n
vals(i) = read_val()
end do
o = process(vals)
end function read_and_process
Используется возможность Fortran 90 для проверки интерфейсов процедур во время компиляции; с другой стороны, если функции используют интерфейс вызова, который был до Fortran 90, сначала должны быть объявлены (внешние) функции, а длина массива должна быть явно передана в качестве аргумента (как в C):
function read_and_process(n) result(o)
integer,intent(in)::n
real::o
real,dimension(n)::vals
real::read_val, process
integer::i
do i = 1,n
vals(i) = read_val()
end do
o = process(vals,n)
end function read_and_process
Cobol
Следующий фрагмент на языке
COBOL
объявляет массив записей переменной длины
DEPT-PERSON
, имеющий длину (количество элементов), заданную значением
PEOPLE-CNT
:
DATA DIVISION.
WORKING-STORAGE SECTION.
01 DEPT-PEOPLE.
05 PEOPLE-CNT PIC S9(4) BINARY.
05 DEPT-PERSON OCCURS 0 TO 20 TIMES DEPENDING ON PEOPLE-CNT.
10 PERSON-NAME PIC X(20).
10 PERSON-WAGE PIC S9(7)V99 PACKED-DECIMAL.
Массивы переменной длины в языке
COBOL
, в отличие от других языков, упомянутых здесь, безопасен, потому что
COBOL
требует указания максимального размера массива — в этом примере
DEPT-PERSON
не может содержать более 20 элементов, независимо от значения
PEOPLE-CNT
.
C#
Следующий фрагмент на языке
C#
объявляет массив целых чисел переменной длины. До версии C# 7.2 требовался указатель на массив в «небезопасном» контексте. Ключевое слово
unsafe
требует, чтобы сборка, содержащая этот код, была помечена как небезопасная.
unsafe void DeclareStackBasedArrayUnsafe(int size)
{
int *pArray = stackalloc int[size];
pArray[0] = 123;
}
C# версии 7.2 и более поздних версий позволяет выделять массив без ключевого слова
unsafe
с помощью функции Span
.
void DeclareStackBasedArraySafe(int size)
{
Span<int> stackArray = stackalloc int[size];
stackArray[0] = 123;
}
Object Pascal
В этом языке массив переменной длины называется динамическим массивом. Объявление такой переменной аналогично объявлению статического массива, но без указания его размера. Размер массива задаётся во время его использования.
program CreateDynamicArrayOfNumbers(Size: Integer);
var
NumberArray: array of LongWord;
begin
SetLength(NumberArray, Size);
NumberArray[0] := 2020;
end.
Удаление содержимого динамического массива выполняется путём присвоения ему нулевого размера.
...
SetLength(NumberArray, 0);
...
Ссылки
- ↑ . Архивировано из 26 января 2018 года.
- . Дата обращения: 16 августа 2022. 27 августа 2022 года.
- ISO 9899:2011 Programming Languages – C 6.7.6.2 4.
- . Дата обращения: 16 августа 2022. 28 мая 2022 года.
- § 6.10.8.3 of the C11 standard (n1570.pdf)
- . lkml.org . Дата обращения: 16 августа 2022. 17 августа 2022 года.
- (англ.) . www.phoronix.com . Дата обращения: 16 августа 2022. 23 июня 2022 года.
- §6.5.3.4 and §7.20.3 of the C11 standard (n1570.pdf)
- . Microsoft. Дата обращения: 16 августа 2022. 26 августа 2022 года.
- 2021-02-17
- 1