Как генерируются инструкции массива переменной длины?

Массивы переменной длины поддерживаются в C:

int main(){
   int num = 5;
    int arr[num];
    return 0;
}

Я понимаю, что arr выделяется во время выполнения. Как это достигается? Вызывает ли он функцию времени выполнения C для выделения байтов? Поскольку объем выделения неизвестен во время компиляции, инструкции для распределения стека существовать не должны.

В качестве побочного вопроса: рекомендуется ли использовать их вместо malloc и выделения кучи, поскольку VLA официально не поддерживаются в C++?

Редактировать:

Похоже, это может быть реализовано с использованием alloca, который размещает кадр в стеке.


person Community    schedule 21.04.2020    source источник
comment
VLA также необязательно поддерживаются в C11 (это означает, что не все компиляторы, совместимые с C11, должны его поддерживать).   -  person UnholySheep    schedule 21.04.2020
comment
В вашем примере сумма выделения известна во время компиляции, когда arr выделяется, num может быть только 5. Но вы можете scanf("%d", &num); сделать ее неизвестной.   -  person Schwern    schedule 21.04.2020
comment
И, возможно, актуально: stackoverflow. ком/вопросы/21182307/   -  person UnholySheep    schedule 21.04.2020


Ответы (3)


То, как выполняется выделение VLA, зависит от конкретной реализации — те, с которыми я знаком, выделяют из стека, но им это не обязательно.

VLA полезны, но только в очень ограниченных случаях. Поскольку их размер неизвестен до времени выполнения, они не могут быть членами struct типов, они не могут иметь static продолжительность хранения, а их размеры могут быть ограничены. Если вам нужно временное хранилище, которое не слишком велико (порядка нескольких килобайт или около того), и вы заранее не знаете, насколько оно велико, и ему не нужно сохраняться за пределами текущей области, тогда VLA могут быть удобны и с ними проще иметь дело, чем с динамической памятью.

Однако с 2011 года поддержка VLA является необязательной, поэтому я бы не стал слишком на них полагаться.

person John Bode    schedule 21.04.2020
comment
Поскольку он существовал первым, я предпочитал использовать alloca.h et. др. потому что обычно мне нужен только одномерный массив, и он более совместим с malloc, если мне придется переключиться позже, потому что размер больше не будет соответствовать [хорошо] стеку. Но, просто любопытно, что является/было основанием для изменения поддержки VLA на необязательную? - person Craig Estey; 21.04.2020
comment
@CraigEstey: я думаю, что они вызывают проблемы во встроенных средах, где пространство стека ограничено. - person John Bode; 21.04.2020

Не только, как вы правильно говорите

VLA официально не поддерживаются в C++.

но они также были переведены в категорию необязательная функция начиная с версии C11 (хотя они были добавлены только в версии C99!). На самом деле это причина их не использовать с целью иметь переносимый код.


Детали распределения памяти, к сожалению, зависят от реализации. Они обычно размещаются в стеке большинством компиляторов как переменные автоматического хранения (согласно моим исследованиям и моему личному опыту), но также могут размещаться в куче.

Наличие массивов, выделенных в стеке, может привести к проблемам с переполнением стека, особенно во встроенной среде. Я предлагаю посетить этот вопрос (о том, что VLA не поддерживаются в стандартах C++); в частности, действительно интересен этот ответ от @Quuxplusone (выделено мной):

Массивы переменной длины в C99 были ошибкой. Чтобы поддержать VLA, C99 пришлось [...] пойти на уступки здравому смыслу.

Менее важно в мире C++, но чрезвычайно важно для целевой аудитории C программистов встроенных систем, объявление VLA означает захват сколь угодно большого куска вашего стека. Это гарантированное переполнение стека и сбой. (Каждый раз, когда вы объявляете int A[n], вы неявно утверждаете, что у вас есть 2 ГБ свободного стека. В конце концов, если вы знаете, что здесь n определенно меньше 1000, то вы бы просто объявили int A[1000].


Насколько я понимаю, их основное преимущество состоит в наличии массивов переменной длины с локальной областью действия, чего нельзя достичь с его альтернативами:

/* Fixed length array, either global or local */
int arr[100];

/* Dynamic allocation */
int * arr = malloc (100 * sizeof (int));

Во всяком случае, в большинстве случаев разработчик либо

  • Знает, какой максимальный размер может иметь VLA. Так почему бы не выделить его статически с фиксированной длиной?
  • Не имеет контроля над максимальным размером, поэтому им придется выполнить проверку работоспособности, чтобы избежать переполнения стека. Так почему бы не ограничить его размер выделением фиксированной длины?
person Roberto Caboni    schedule 21.04.2020

В качестве побочного вопроса, является ли хорошей практикой использовать их по распределению malloc и кучи,

Нет, вместо этого рассмотрите возможность использования элементов гибкого массива в качестве последнего элемента ваши struct-s переменного размера. Убедитесь, что malloc не дал сбой во время выполнения.

Во встроенном программировании VLA-ы полезны, когда вы можете гарантировать, что они достаточно малы, поскольку вы не хотите взорвать свой стек вызовов. Минимум предшествует int arr[num]; чему-то вроде assert(num>0 && num<100); или еще лучше, добавьте такую ​​проверку во время выполнения. Вы можете использовать такие инструменты, как Frama-C, чтобы доказать это статически.

person Basile Starynkevitch    schedule 22.04.2020