Массив переменной длины
В компьютерном программировании массив переменной длины ( VLA ), также называемый переменным размером или размером во время выполнения , представляет собой структуру данных массива , длина которой определяется во время выполнения , а не во время компиляции . [1] В языке C говорят, что VLA имеет изменяемый тип данных , который зависит от значения (см. Зависимый тип ).
Основная цель VLA — упростить программирование числовых алгоритмов .
Языки программирования, поддерживающие VLA, включают Ada , ALGOL 68 (для негибких строк), APL , C99 (хотя впоследствии в C11 они были отнесены к условной функции, которую реализации не обязаны поддерживать); [2] [3] на некоторых платформах VLA раньше можно было реализовать с помощью alloca()
или подобные функции) и C# в небезопасном режиме , выделенные в стеке (как массивы ), COBOL , Fortran 90, J и Object Pascal (язык, используемый в Delphi и Lazarus , который использует FPC).
Расширяемые массивы (также называемые динамическими массивами ), как правило, более полезны, чем VLA, поскольку динамические массивы могут делать все, что могут делать VLA, а также поддерживают рост массива во время выполнения. По этой причине многие языки программирования ( JavaScript , Java , Python , R и т. д.) поддерживают только растущие массивы. Даже в языках, поддерживающих массивы переменной длины, часто рекомендуется избегать использования массивов переменной длины (на основе стека) и вместо этого использовать ( на основе кучи ). динамические массивы [4]
Память
[ редактировать ]Распределение
[ редактировать ]- Коллекция компиляторов GNU (GCC) для C выделяет память для VLA с автоматической продолжительностью хранения в стеке . [5] Это более быстрый и простой вариант по сравнению с выделением кучи, который используется большинством компиляторов.
- VLA также можно разместить в куче и получить к ним внутренний доступ с помощью указателя на этот блок.
Выполнение
[ редактировать ]С99
[ редактировать ]Следующая функция 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 параметр длины должен идти перед параметром массива переменной длины при вызове функций. [1] В С11 __STDC_NO_VLA__
макрос определяется, если VLA не поддерживается. [6] Стандарт C23 снова делает типы VLA обязательными. Необязательно создавать только объекты VLA с автоматическим сроком хранения. [7] GCC имел VLA в качестве расширения до C99, которое также распространяется на его диалект C++.
Линус Торвальдс в прошлом выражал свое недовольство использованием VLA для массивов с заранее определенными маленькими размерами, поскольку он генерирует ассемблерный код более низкого качества. [8] Ядро Linux 4.20 фактически не содержит VLA. [9]
Хотя в C11 явно не указан предел размера VLA, некоторые полагают, что он должен иметь тот же максимальный размер, что и все другие объекты, т. е. SIZE_MAX в байтах. [10] Однако это следует понимать в более широком контексте ограничений среды и платформы, таких как типичный размер страницы защиты стека 4 КиБ, что на много порядков меньше, чем SIZE_MAX.
Объект VLA можно использовать с динамическим хранилищем, используя указатель на массив.
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;
Фортран 90
[ редактировать ]Эквивалентная Фортрана 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 объявляет массив записей переменной длины. 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 . VLA, в отличие от других упомянутых здесь языков, безопасен, поскольку COBOL требует указания максимального размера массива В этом примере DEPT-PERSON
не может иметь более 20 предметов, независимо от значения PEOPLE-CNT
.
С#
[ редактировать ]Следующий фрагмент C# объявляет массив целых чисел переменной длины. До версии C# 7.2 требовался указатель на массив, требующий «небезопасного» контекста. Ключевое слово unsafe требует, чтобы сборка, содержащая этот код, была помечена как небезопасная.
unsafe void DeclareStackBasedArrayUnsafe(int size)
{
int *pArray = stackalloc int[size];
pArray[0] = 123;
}
C# версии 7.2 и более поздних версий позволяют выделять массив без ключевого слова unsafe с помощью функции Span. [11]
void DeclareStackBasedArraySafe(int size)
{
Span<int> stackArray = stackalloc int[size];
stackArray[0] = 123;
}
Объектный Паскаль
[ редактировать ]Динамические массивы Object Pascal размещаются в куче. [12]
На этом языке это называется динамическим массивом. Объявление такой переменной аналогично объявлению статического массива, но без указания его размера. Размер массива указывается на момент его использования.
program CreateDynamicArrayOfNumbers(Size: Integer);
var
NumberArray: array of LongWord;
begin
SetLength(NumberArray, Size);
NumberArray[0] := 2020;
end.
Удаление содержимого динамического массива осуществляется присвоением ему нулевого размера.
...
SetLength(NumberArray, 0);
...
Ссылки
[ редактировать ]- ^ Перейти обратно: а б «Массивы переменной длины» . Архивировано из оригинала 26 января 2018 г.
- ^ «Переменная длина – использование коллекции компиляторов GNU (GCC)» .
- ^ ISO 9899:2011 Языки программирования – C 6.7.6.2 4.
- ^ Раймонд, Эрик С. (2000). «Практика выпуска программного обеспечения Raymond: 6. Хорошая практика разработки» . Проект документации Linux .
- ^ «Параметры генерации кода — компилятор GNU Fortran» .
- ^ § 6.10.8.3 стандарта C11 (n1570.pdf)
- ^ § 6.10.9.3 стандарта C23 (n3054.pdf)
- ^ Торвальдс, Линус (7 марта 2018 г.). «LKML: Линус Торвальдс: Re: удаление VLA (было Re: [RFC 2/2] блеск: используйте VLA_SAFE)» . Ядро Linux (список рассылки).
- ^ «Ядро Linux теперь не содержит VLA: победа в плане безопасности, меньше накладных расходов и лучшее для Clang — Phoronix» . www.phoronix.com .
- ^ §6.5.3.4 и §7.20.3 стандарта C11 (n1570.pdf)
- ^ «Оператор stackalloc (ссылка на C#)» . Майкрософт.
- ^ Майкл Ван Каннейт. «Справочное руководство по Free Pascal: Динамические массивы» .