Как компиляторы обрабатывают массивы переменной длины

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

#include int main(){ int n; std::cin>>n; int a[n]; } 

Из того, что я узнал, в C все значения инициализатора должны быть постоянными, так что компилятор знает, сколько памяти зарезервировано внутри функции, обычно путем вычитания указателя стека, чтобы разместить количество элементов, удерживаемых массивом.

Это имеет смысл для меня. Однако я не совсем понимаю, как компиляторы рассматривают вышеуказанную программу, так как она работает с G ++ (MinGW), но не работает с компилятором C, Microsoft C ++. Я подозреваю, что GCC выделяет память на кучу через нестандартное расширение, но я не уверен в этом.

Кроме того, компилятор Microsoft не известен как совместимый со стандартами, поэтому я не удивлюсь, если он может быть ошибочным в том, как он относится к вышеуказанной программе.

В версии C99 стандарта C допускаются массивы переменной длины. Однако они не разрешены ни в одной версии C ++; вы видите расширение G ++. Обратите внимание, что компилятор Microsoft C не поддерживает полностью C99; поскольку G ++ поддерживает C99, достаточно просто применить поддержку VLA к C ++ в качестве расширения.

Что касается того, как компилятор обычно реализует VLA, это то же самое, что и alloca() (за исключением того, что он должен поддерживать размер вокруг sizeof ) – компилятор сохраняет исходный указатель стека, а затем настраивает его на сколько-нибудь байты он вычисляет, что он необходимо. Недостатком является ввод функции, и выход немного сложнее, поскольку компилятору необходимо сохранить место для сброса указателя стека, а не просто корректировать фиксированные константы.

Нет абсолютно никакой проблемы с «вычитанием указателя стека» во время выполнения, значением времени выполнения. Именно так компиляторы обычно реализуют массивы переменной длины. Они «вычитают указатель стека» во время выполнения, когда фактический размер массива уже известен. Вот и все. (Нет необходимости выделять память на кучу, и я не знаю, что заставило вас подозревать это в GCC.).

Такая функциональность была доступна задолго до того, как VLA стали частью языка. Функция [нестандартная] alloca делает именно это. Единственное различие заключается в том, что память, выделенная alloca , автоматически освобождается при выходе из функции, тогда как локальные VLA должны подчиняться стандартным правилам жизни на основе блоков. Последнее не является проблемой вообще, так как блочное гнездо в виде стека.

Другими словами, для значения времени выполнения n декларация

 int a[n]; 

по существу, переведена на нечто вроде

 int *a = alloca(n * sizeof *a); 

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

Никакая версия C ++ не допускает массив переменной длины. Только C99 допускает это.

GCC позволяет использовать его как расширение.

 int main(){ int n; std::cin>>n; int a[n]; } 

Это не законный C ++. G ++ принимает переменную длину массива как расширение, но VLA не являются частью стандарта C ++. Они являются частью стандарта C99, поддерживаемого GCC (я не уверен, в какой степени), но MSVC этого не делает.

  • Можете ли вы создавать пользовательские операторы на C ++?
  • SFINAE работает в обратном типе, но не как параметр шаблона
  • Почему Синглтон считается анти-шаблоном?
  • Если я хочу специализироваться только на одном методе в шаблоне, как мне это сделать?
  • Распространение «typedef» из основанного на производный class для «шаблона»
  • Что именно «нарушено» с помощью двухфазного экземпляра шаблона Microsoft Visual C ++?
  • Как передать функцию шаблона в списке аргументов шаблона
  • Примеры C ++ SFINAE?
  • Ограничить параметр шаблона C ++ для подclassа
  • В чем преимущество шаблона без логики (например, усы)?
  • Как реализовать макрос (или шаблон) no-op в C ++?
  • Давайте будем гением компьютера.