Открытые массивы
{
"title": "Открытые массивы в Delphi: техническая реализация, спецификации и отличия от альтернатив",
"keywords": "открытые массивы Delphi, динамические массивы, array of const, TVarRec, управление памятью, низкоуровневые отличия, передача параметров, синтаксис открытых массивов",
"description": "Технический анализ открытых массивов в Delphi: внутренние структуры TVarRec, отличия от динамических массивов по управлению памятью, требования к передаче в подпрограммы и совместимость по типам (2026).",
"html_content": "Спецификация открытых массивов: синтаксис и внутреннее представление
Открытые массивы (open arrays) в Delphi представляют собой механизм передачи в подпрограмму последовательности элементов без фиксации длины на этапе компиляции. Технически это не отдельный тип данных, а способ описания параметра — директива array of T в заголовке функции или процедуры. Компилятор транслирует такой параметр в структуру, состоящую из указателя на первый элемент и 32-битного счетчика количества элементов (тип System.TArrayInfo в RTL). Для совместимости с Win64 длина хранится как NativeInt (8 байт), но в 32-битных сборках поле остается 4-байтовым. Это критически важно при межмодульном взаимодействии: передача открытого массива через границу DLL требует строгого выравнивания стека, иначе возникает AV (access violation) при разыменовании указателя.
Отличие от динамических массивов: управление памятью и счетчик ссылок
Главное техническое отличие открытого массива от динамического (array of Integer как тип данных) — способ управления памятью. Динамический массив — это ссылочный тип с полем счетчика ссылок (refCount), расположенным перед началом данных (отрицательное смещение -8 от указателя на элемент). При передаче такой переменной в подпрограмму с параметром array of T происходит автоматическое создание временной копии данных стека (stack-allocated copy) без изменения счетчика ссылок. Для малых массивов (≤256 элементов) компилятор может использовать инлайн-развертывание (inlined stack copy), избегая вызова _CopyArray из System.pas. Для больших массивов — вызов процедуры копирования с блочным переносом (движок REP MOVSD/MOVSQ на x64). Это отличие ведет к дополнительным накладным расходам: вызов Length() для динамического массива — это чтение скрытого поля (-4 от указателя), для открытого — чтение из регистра или временной переменной, что быстрее на 1–2 такта.
Материалы и реализация: TVarRec и массив констант
Особый случай — открытый массив констант array of const. Технически это массив записей TVarRec (16 байт в 32-битной версии, 24 — в 64-битной). Каждый элемент содержит поле типа (VType: TVarType) и объединение (variant part): для целых — VInteger: Integer, для строк — VString: Pointer (AnsiString) или VUnicodeString: Pointer (UnicodeString). Выравнивание: в 32-bit — 4 байта, в 64-bit — 8 байт; компилятор добавляет паддинг до размера 16/24. Критическое ограничение: передача array of const с элементами типа Currency требует явного приведения через System.CurrToStr — иначе данные интерпретируются как Int64 со сдвигом битов.
Производственные стандарты: требования к коду и оптимизации
При использовании открытых массивов в промышленной разработке на Delphi (компиляторы 10.x–12.x и RAD Studio 12 Athens) необходимо соблюдать следующие соглашения:
- Для параметров
array of Tиспользовать модификаторconst, если массив не модифицируется — компилятор в этом случае может передать данные по ссылке (какvar, без копирования), что снижает объем стека и ускоряет вызов на 15–20% для массивов размером более 1 КБ. - Избегать передачи открытого массива, созданного из литерала (
procedure([1,2,3])) в рекурсивные функции — каждый вызов приводит к повторному размещению констант в стеке, увеличивая риск переполнения стека (stack overflow) при глубине более 500 вызовов. - Для совместимости с ABI (Application Binary Interface) сторонних библиотек, написанных на C/C++, структура открытого массива должна быть заменена на запись с полями
Data: PointerиCount: NativeInt— в противном случае смещение служебных полей будет несовместимо (дело в том, что в C массив не содержит встроенного счетчика).
Различия с другими средами: сравнение с массивами в C++/CLI и Free Pascal
В Free Pascal (FPC) открытые массивы реализованы иначе: параметр array of T передается через скрытый параметр — указатель на структуру TOpenArrayRec, содержащую три поля: $type (тип элемента), high (верхняя граница) и data (указатель). Размер этой структуры — 12 байт в 32-битной, 24 байта в 64-битной версии (из-за выравнивания). В Delphi же используется более легковесный подход — два регистра (указатель и длина). Это дает выигрыш в скорости вызова для коротких массивов (до 10 элементов): Delphi — 4–6 тактов, FPC — 8–12 тактов. Однако в FPC поддерживается динамическое изменение границ через SetLength внутри подпрограммы (только для локальных копий), что в Delphi недопустимо — SetLength на открытом массиве вызывает исключение EInvalidOperation.
Качество и верификация: статический анализ и граничные случаи
Статический анализатор Delphi (Code Insight) не проверяет выход за границы открытого массива на этапе компиляции — проверки range checking генерируются в рантайме и могут быть отключены директивой {$R-}. Для критического ПО (авионика, медицинские системы) рекомендуется оборачивать вызовы в обертки с явной проверкой длины через Low()/High(). Пример корректной проверки: if (Index >= Low(arr)) and (Index <= High(arr)) then …. Это обязательное условие при сертификации по стандарту DO-178C Level A.
Изготовление: ручное и автоматизированное создание
Для создания открытого массива без лишних копирований используется инициализация через конструктор System.TArray.Create\ — этот метод размещает элементы в динамической памяти и возвращает динамический массив, который затем автоматически преобразуется в открытый при передаче. Производительность: для массивов размером 1000 Integer элементы, конструктор в 1.7 раза быстрее поэлементного присваивания (arr[0] := 1; arr[1] := 2; …), так как использует заполнение через FillChar с одним вызовом SysGetMem. Для сериализации в JSON через System.JSON открытые массивы непригодны — требуется конвертация в TJSONArray через цикл с типизированным приведением.
Добавлено: 27.04.2026
