В чем вообще смысл VLA?

Я понимаю, что такое массивы переменной длины и как они реализованы. Этот вопрос о том, почему они существуют.

Мы знаем, что VLA разрешены только внутри функциональных блоков (или прототипов) и что они в принципе не могут быть нигде, кроме как в стеке (при условии нормальной реализации): C11, 6.7.6.2-2:

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

Возьмем небольшой пример:

void f(int n)
{
    int array[n];
    /* etc */
}

есть два случая, о которых нужно позаботиться:

  • #P6# <блочная цитата> #P7#
  • n > stack_space_left / element_size: Не существует стандартного способа узнать, сколько места в стеке осталось (поскольку стека не существует, если говорить о стандарте). Так что этот тест невозможен. Единственное разумное решение — предопределить максимально возможный размер для n, скажем, N, чтобы избежать переполнения стека.

Другими словами, программист должен убедиться, что 0 < n <= N для некоторых N по выбору. Тем не менее, программа все равно должна работать для n == N, так что с тем же успехом можно объявить массив с постоянным размером N, а не с переменной длиной n.

Я знаю, что VLA были введены для замены alloca (как также упоминалось в этом ответе), но по сути они одинаковы вещь (выделить память переменного размера в стеке).

Итак, вопрос в том, почему alloca и, следовательно, VLA существовали и почему они не устарели? Мне кажется, что единственным безопасным способом использования VLA является ограниченный размер, и в этом случае выбор нормального массива с максимальным размером всегда является жизнеспособным решением.


person Shahbaz    schedule 20.03.2014    source источник
comment
alloca не входит в стандарт. И VLA стал необязательным в C11. И то, и другое небезопасно, но большие массивы постоянного размера времени компиляции также небезопасны.   -  person keltar    schedule 20.03.2014
comment
@presiuslitelsnoflek, да, переполнения стека можно получить разными способами. Очевидно, что ограниченный размер массивов переменной длины не предотвращает бесконечную рекурсию. но неограниченный размер для VLA означает возможное переполнение стека. Я не понимаю, как это связано с тем, что единственный безопасный способ использования VLA, как мне кажется, с ограниченным размером не соответствует действительности.   -  person Shahbaz    schedule 20.03.2014
comment
@keltar, спасибо, но я знаю об этом. Вопрос заключался в том, почему они вообще использовали alloca? и почему, несмотря на такие проблемы, люди продолжали использовать его до такой степени, что он нашел свое место в стандарте?   -  person Shahbaz    schedule 20.03.2014
comment
n >= 0 можно решить, превратив n в size_t (в любом случае это то, что вы должны использовать для размеров). Если звонящий вводит отрицательное число, то последующие беспорядки ложатся на их ответственность.   -  person Kninnug    schedule 20.03.2014
comment
Они? Кто они? alloca отлично подходит для небольших буферов, например. printf - лично я не хочу, чтобы он использовал malloc или другое распределение кучи.   -  person keltar    schedule 20.03.2014
comment
@keltar, если бы никто не использовал alloca, не было бы причин для того, чтобы он стал стандартом (в форме VLA), поэтому должно быть достаточно людей, которые продолжали его использовать. Кроме того, alloca отлично подходит для небольших буферов, что означает, что вы можете гарантировать, что буферы будут маленькими (если вы не можете, то они больше не являются маленькими буферами), т.е. вы можете гарантировать, что существует верхняя граница . Если вы можете это сделать, почему бы не получить массив фиксированного размера такого размера? Наконец, у меня есть реализация fprintf, и я не понимаю, почему вам может понадобиться VLA в ее реализации.   -  person Shahbaz    schedule 20.03.2014
comment
@Kninnug, да, я должен был сделать это size_t (стандарт использует int в своих примерах, и я просто использовал int, не задумываясь). Тем не менее, size_t по-прежнему не защищает от n == 0, поэтому вам все равно нужна проверка, будь то if (n == 0) fail или if (n <= 0) fail.   -  person Shahbaz    schedule 20.03.2014
comment
@keltar, просто для ясности: я не говорю, что использование VLA следует заменить на malloc. Это было бы безумием.   -  person Shahbaz    schedule 20.03.2014
comment
@Shahbaz, вы на самом деле не знаете длину результирующей отформатированной строки; это можно сделать с помощью итерации по буферу статического размера, но это сложно. Что касается причин alloca - в случае рекурсии это уменьшит использование стека, поэтому вы можете выполнять больше вызовов до достижения предела стека; чтение ввода из файла, который, как известно, короткий; что бы ни. У вас всегда есть ограничения; у вас нет защиты стека для массивов константного размера, но вы не спрашиваете о них :-). Я лично предпочитаю alloca, потому что он явно указывает, что программист хотел сделать.   -  person keltar    schedule 20.03.2014
comment
Что действительно не так с VLA размером 0?   -  person harold    schedule 20.03.2014
comment
@keltar, вам не нужно знать длину результирующей строки. Вы выводите char за char, который буферизуется на более низком уровне. т.е. fputc записывает в буфер, а fprintf использует fputc. Что касается рекурсии, VLA не имеет смысла. Если ваш ввод большой, вы можете рекурсировать меньше, а если он маленький, вы можете больше рекурсировать? Требование часто наоборот.   -  person Shahbaz    schedule 20.03.2014
comment
Это говорит о том, что VLA иногда может иметь определенные преимущества в производительности: пример массива переменной длины">programmers.stackexchange.com/questions/190546/   -  person soorajmr    schedule 20.03.2014
comment
Вынужденные выбирать между переполнением буфера стека и сбоем сегментации, многие программисты, по-видимому, предпочитают, чтобы их программа терпела неудачу при переполнении буфера. Это серьезная проблема, переполнение буфера не поддается диагностике и является серьезной проблемой безопасности, VLA пытаются решить эту проблему.   -  person Hans Passant    schedule 20.03.2014
comment
@harold, я думаю, дело в том, что массив размера 0 ничего не может содержать. Если вы обращаетесь к любому индексу такого массива, это вызывает неопределенное поведение. Итак, какой смысл иметь его?   -  person Shahbaz    schedule 20.03.2014
comment
@Shahbaz, чтобы избежать особого случая - например, вполне естественно выделить что-то с элементами n, а затем перебрать элементы n, если n = 0 это все еще не проблема. Да и вообще какой смысл сильно не аргументировать в пользу запрета чего-либо.   -  person harold    schedule 20.03.2014
comment
@HansPassant, можешь немного расширить? Если вам нужна такая большая память, что вы рискуете переполнить стек, вам все равно придется использовать malloc. Я имею в виду, что вы не выбираете между переполнением стека и segfault. Оба плохие. Вы можете обойтись и без того.   -  person Shahbaz    schedule 20.03.2014
comment
@Shahbaz больше похоже на то, что вы можете вызывать его с небольшими данными, даже если вы уже очень глубоко погрузились в рекурсию (вызванную другими функциями). C очень близок к аппаратному обеспечению, здорово, когда он позволяет вам что-то делать, даже если вы на самом деле его не используете. Но, к сожалению, не всегда хорошо, если это подразумевается.   -  person keltar    schedule 20.03.2014
comment
@гарольд, да, я понимаю и согласен. Но, наверное, это несколько усложняет ситуацию. Возьмем, к примеру, int array[N] = {0}. Если стандарт требует, чтобы N было строго положительным, все в порядке. Если он позволяет N быть равным нулю, он должен затем создать угловой случай, говорящий, что в таком случае инициализация невозможна. Это было для массивов фиксированного размера (C11, 6.7.6.2-1). Вероятно, из-за симметрии они добавили такое же правило для VLA.   -  person Shahbaz    schedule 20.03.2014
comment
@Shahbaz хорошо, это имеет смысл   -  person harold    schedule 20.03.2014
comment
Без VLA, alloca() и рекурсии верхняя граница глубины стека может быть определена еще до запуска программы. Таким образом, при вызове программы может быть назначено достаточное пространство стека. Рекурсия препятствует простому анализу необходимой глубины стека. Тем не менее рекурсия разрешена (во многих средах). ИМО, использование VLA принципиально не сопряжено с большим риском, чем рекурсия. Если коду не нужны риски VLA, запретите и рекурсию.   -  person chux - Reinstate Monica    schedule 20.03.2014
comment
@chux, я действительно думал об этом. Честно говоря, если вы не можете убедиться, что рекурсивная функция является хвостовой рекурсией, ее нельзя использовать для любого достаточно большого набора данных. Например, многие графовые алгоритмы (яркий пример рекурсивных функций) в конечном итоге реализуются как нерекурсивные по этой причине. В функциональных языках крайне важно сделать функции хвостовой рекурсией. Так что нет, рекурсия тоже не такой уж хороший инструмент. Однако рекурсия естественным образом связана с тем, как вызываются функции. Запрещение их является довольно искусственным.   -  person Shahbaz    schedule 20.03.2014
comment
Другим распространенным случаем (теоретически) рекурсивных функций являются синтаксические анализаторы, но они также в конечном итоге реализуются как нерекурсивные в любом серьезном синтаксическом анализаторе.   -  person Shahbaz    schedule 20.03.2014
comment
@chux, рекурсия была бы полезна, если проблема изначально ограничена. Например, в графическом интерфейсе у вас не будет огромной рекурсии по виджетам просто потому, что никто не настолько сумасшедший, чтобы разрабатывать такой графический интерфейс. Но в целом рекурсия не очень хорошее решение.   -  person Shahbaz    schedule 20.03.2014
comment
Запрет рекурсии не искусственный. Обычно их запрещают во встроенных конструкциях. Но моя главная мысль не в достоинствах/недостатках рекурсии, а в том, что рекурсия и VLA одинаково бросают вызов стеку.   -  person chux - Reinstate Monica    schedule 20.03.2014
comment
технически VLA не обязательно должны находиться в стеке   -  person AShelly    schedule 05.12.2018
comment
Для VLA не существует надежной модели безопасности, и ваш код подвержен труднодиагностируемым ошибкам. Одним из хорошо зарекомендовавших себя проектов C является ядро ​​Linux, в котором нет VLA с 2018 года; Разработчики ядра решили стереть все использование VLA, потому что головные боли (безопасность, несоответствия производительности и т. д.) перевешивают преимущества. Примечание. Сообщество LLVM также отказалось от VLA, поэтому clang поддерживает их только в пределах C99, не более того (поля VLA в структуре и т. д., которые поддерживает GCC). После такого рода дискуссий и выявленных случаев комитет решил сделать VLA необязательным в ISO C11.   -  person vulcan raven    schedule 06.11.2020


Ответы (5)


По не совсем понятным мне причинам почти каждый раз, когда в обсуждении всплывает тема C99 VLA, люди начинают говорить преимущественно о возможности объявления массивов run-time size локальными объектами (т.е. создания их «на стеке "). Это довольно удивительно и вводит в заблуждение, поскольку этот аспект функциональности VLA — поддержка объявлений локальных массивов — оказывается скорее вспомогательной, вторичной возможностью, предоставляемой VLA. На самом деле это не играет существенной роли в том, что может сделать VLA. В большинстве случаев вопрос о местных декларациях VLA и сопутствующих им потенциальных ловушках выдвигается на передний план критиками VLA, которые используют его как «соломенное чучело», предназначенное для того, чтобы сорвать дискуссию и увязнуть в маловажных деталях.

Суть поддержки VLA в C заключается, прежде всего, в революционном качественном расширении языковой концепции type. Он предполагает введение таких принципиально новых видов типов, как вариабельно изменяемые типы. Практически каждая важная деталь реализации, связанная с VLA, на самом деле привязана к его типу, а не к объекту VLA как таковому. Именно введение в язык вариабельно изменяемых типов и составляет основную часть пресловутого пирога VLA, а возможность объявлять объекты таких типов в локальной памяти — не более чем незначительная и справедливо бесполезная вишенка на торте.

Подумайте об этом: каждый раз, когда кто-то объявляет что-то подобное в своем коде

/* Block scope */
int n = 10;
...
typedef int A[n];
...
n = 5; /* <- Does not affect `A` */

связанные с размером характеристики изменяемого типа A (например, значение n) завершаются в тот самый момент, когда управление проходит через вышеприведенное объявление typedef. Любые изменения значения n, сделанные дальше по строке (ниже этого объявления A), не влияют на размер A. Остановитесь на секунду и подумайте, что это значит. Это означает, что реализация должна связать с A скрытую внутреннюю переменную, которая будет хранить размер типа массива. Эта скрытая внутренняя переменная инициализируется из n во время выполнения, когда управление переходит к объявлению A.

Это придает вышеприведенному объявлению typedef довольно интересное и необычное свойство, чего мы раньше не видели: это объявление typedef генерирует исполняемый код (!). Более того, он генерирует не просто исполняемый код, а критически важный исполняемый код. Если мы каким-то образом забудем инициализировать внутреннюю переменную, связанную с таким объявлением typedef, мы получим «сломанный»/неинициализированный псевдоним typedef. Важность этого внутреннего кода является причиной того, что язык накладывает некоторые необычные ограничения на такие вариабельно изменяемые объявления: язык запрещает передачу управления в их область видимости из-за пределов их области видимости.

/* Block scope */
int n = 10;
goto skip; /* Error: invalid goto */

typedef int A[n];

skip:;

Еще раз обратите внимание, что приведенный выше код не определяет никаких массивов VLA. Он просто объявляет, казалось бы, невинный псевдоним для вариабельно изменяемого типа. Тем не менее, недопустимо перепрыгивать через такое объявление typedef. (Мы уже знакомы с такими ограничениями, связанными с переходом, в C++, хотя и в других контекстах).

Генерирующий код typedef, typedef, требующий инициализации во время выполнения, является значительным отклонением от того, что typedef есть в "классическом" языке. (Это также представляет собой серьезное препятствие на пути внедрения VLA в C++.)

Когда кто-то объявляет фактический объект VLA, в дополнение к выделению фактической памяти массива компилятор также создает одну или несколько скрытых внутренних переменных, которые содержат размеры рассматриваемого массива. Нужно понимать, что эти скрытые переменные связаны не с самим массивом, а скорее с его вариабельно изменяемым типом.

Одним из важных и примечательных следствий такого подхода является следующее: дополнительная информация о размере массива, связанная с VLA, не встраивается непосредственно в объектное представление VLA. На самом деле он хранится помимо массива как данные "sidecar". Это означает, что объектное представление (возможно, многомерного) VLA полностью совместимо с объектным представлением обычного классического массива размера времени компиляции той же размерности и тех же размеров. Например

void foo(unsigned n, unsigned m, unsigned k, int a[n][m][k]) {}
void bar(int a[5][5][5]) {}

int main(void)
{
  unsigned n = 5;
  int vla_a[n][n][n];
  bar(a);

  int classic_a[5][6][7];
  foo(5, 6, 7, classic_a); 
}

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

(Примечание. Как обычно, параметры типа массива всегда неявно настраиваются в параметры типа указателя. Это относится к объявлениям параметров VLA точно так же, как и к «классическим» объявлениям параметров массива. Это означает, что в Параметр приведенного выше примера a на самом деле имеет тип int (*)[m][k]. На этот тип не влияет значение n. Я намеренно добавил в массив несколько дополнительных измерений, чтобы сохранить его зависимость от значений времени выполнения.)

Совместимость между VLA и "классическими" массивами как параметрами функций также поддерживается тем фактом, что компилятору не нужно сопровождать вариабельно изменяемый параметр какой-либо дополнительной скрытой информацией о его размере. Вместо этого синтаксис языка заставляет пользователя передавать эту дополнительную информацию в открытом виде. В приведенном выше примере пользователь был вынужден сначала включить параметры n, m и k в список параметров функции. Без объявления n, m и k пользователь не смог бы объявить a (см. также примечание выше о n). Эти параметры, явно переданные в функцию пользователем, принесут информацию о фактических размерах a.

В качестве другого примера, воспользовавшись поддержкой VLA, мы можем написать следующий код

#include <stdio.h>
#include <stdlib.h>

void init(unsigned n, unsigned m, int a[n][m])
{
  for (unsigned i = 0; i < n; ++i)
    for (unsigned j = 0; j < m; ++j)
      a[i][j] = rand() % 100;
}

void display(unsigned n, unsigned m, int a[n][m])
{
  for (unsigned i = 0; i < n; ++i)
    for (unsigned j = 0; j < m; ++j)
      printf("%2d%s", a[i][j], j + 1 < m ? " " : "\n");
  printf("\n");
}

int main(void) 
{
  int a1[5][5] = { 42 }; 
  display(5, 5, a1);
  init(5, 5, a1);
  display(5, 5, a1);

  unsigned n = rand() % 10 + 5, m = rand() % 10 + 5;
  int (*a2)[n][m] = malloc(sizeof *a2);
  init(n, m, *a2);
  display(n, m, *a2);
  free(a2);
}

Этот код призван привлечь ваше внимание к следующему факту: этот код активно использует ценные свойства вариабельно изменяемых типов. Элегантно реализовать без VLA невозможно. Это основная причина, по которой эти свойства отчаянно необходимы в C, чтобы заменить уродливые хаки, которые использовались вместо них ранее. Но в то же время в указанной выше программе в локальной памяти не создается ни одного VLA, а это означает, что этот популярный вектор критики VLA вообще неприменим к этому коду.

По сути, два последних приведенных выше примера — это краткая иллюстрация того, в чем смысл поддержки VLA.

person AnT    schedule 12.01.2019
comment
Учитывая очевидную полезность типов VLA, разочаровывает то, что они стали необязательными в C11. - person ad absurdum; 12.01.2019
comment
Спасибо за это. Пункты о типе VLA были поучительны. Примеры также помогли напомнить о проблеме написания общих матричных математических функций и понять, что решение — VLA. - person Shahbaz; 13.01.2019

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

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

Тем не менее, вот еще одно применение для них, которое я нашел, которое на самом деле не выделяет память в стеке, но упрощает работу с динамическими многомерными массивами. Я продемонстрирую на простом примере:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    size_t n, m;

    scanf("%zu %zu", &n, &m);

    int (*array)[n][m] = malloc(sizeof *array);

    for (size_t i = 0; i < n; ++i)
        for (size_t j = 0; j < m; ++j)
            (*array)[i][j] = i + j;

    free(array);
    return 0;
}
person Shahbaz    schedule 19.01.2015
comment
Почему size_t i = 0; в 4 циклах? - person ; 24.06.2020
comment
@0Люди, size_t, потому что n и m равны size_t (потому что они относятся к размеру). Есть ли что-то еще, что не ясно? - person Shahbaz; 30.06.2020

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

Я думаю, что это может быть причиной того, что VLA принят в C99 (необязательно на C11).


Я хочу прояснить одну вещь: существуют существенные различия между alloca и VLA. Это сообщение указывает на различия:

  • Память, возвращаемая alloca(), действительна до тех пор, пока сохраняется текущая функция. Срок жизни памяти, занимаемой VLA, действителен до тех пор, пока идентификатор VLA остается в области действия.
  • Например, вы можете alloca() памяти в цикле и использовать память вне цикла, VLA исчезнет, ​​потому что идентификатор выходит за пределы области действия, когда цикл завершается.
person haccks    schedule 20.03.2014
comment
На самом деле, вместо «VLA принят в C99 и более поздних версиях», история VLA такова, что он был принят в C99 и стал необязательным в C11. - person Pascal Cuoq; 20.03.2014
comment
Он также правильно обрабатывает sizeof, что, возможно, хорошо или плохо. - person keltar; 20.03.2014
comment
@ПаскальКуок; Я не уверен насчет С11. - person haccks; 20.03.2014
comment
@hackcks См. en.wikipedia.org/wiki/ - person Pascal Cuoq; 20.03.2014
comment
@hackcks, C11, 6.7.6.2-4 говорит ... Массивы переменной длины - это условная функция, которую реализации не должны поддерживать. Возвращаясь к моему вопросу, можете ли вы назвать пример, в котором VLA можно безопасно использовать (убедившись, что он не может вызвать переполнение стека, по крайней мере, с разумной настройкой ulimit), и массив с постоянным размером не может заменить его? - person Shahbaz; 20.03.2014
comment
@ПаскальКуок; Спасибо за ссылку. - person haccks; 20.03.2014
comment
@Шахбаз; да. Получил это сейчас :) - person haccks; 20.03.2014
comment
@Shahbaz Если у вас есть рекурсивный алгоритм, который работает только с частью данного буфера и по каким-то причинам должен его скопировать. Тогда необходимое пространство стека будет datalength * recursiondepth, в то время как с VLA оно может (и будет) меньше, что позволяет использовать более глубокую рекурсию. - person glglgl; 20.03.2014

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

Этот аргумент упускает из виду тот факт, что память является ограниченным ресурсом в системе, разделяемым между многими процессами. Память, расточительно выделенная в одном процессе, недоступна никакому другому (а может и есть, но за счет свопинга на диск).

По тому же аргументу нам не нужно было бы выделять массив во время выполнения, когда мы могли бы статически выделить максимальный размер, который может потребоваться. В конце концов, исчерпание кучи лишь немногим предпочтительнее, чем переполнение стека.

person harmic    schedule 20.03.2014
comment
Хорошо, это имеет смысл. Есть два отличия, которые я считаю существенными. Во-первых, размер стека часто очень мал, а куча довольно велика. На микроконтроллерах кучи часто не существует, и у вас есть строгие ограничения на размер данных, поэтому VLA на микроконтроллерах в любом случае не используются. Другими словами, потеря некоторого размера стека не имеет большого значения, в отличие от потери половины кучи. - person Shahbaz; 20.03.2014
comment
Во-вторых, если у вас закончилась память в куче, malloc прекрасно возвращает NULL, и вы можете принять меры по этому поводу. Вы можете красиво сказать пользователю, например. что операция невозможна или изящно сохранить некоторые состояния, вернуть ресурсы и вернуться в предыдущее хорошее состояние. При переполнении стека вы просто умираете, что плохо. - person Shahbaz; 20.03.2014
comment
malloc может возвращать значение, отличное от NULL, а затем вызывать SIGSEGV при сбое сопоставления памяти, поэтому он не слишком надежен. - person keltar; 20.03.2014
comment
@Shahbaz, вероятно, поэтому VLA являются необязательными в более поздних стандартах. На сервере/настольном компьютере размер стека может увеличиваться (в однопоточной программе, возможно, до тех пор, пока он не попадет в кучу). - person harmic; 20.03.2014
comment
Этот аргумент не работает ни в одной современной операционной системе. Вы выделяете виртуальную память, а не оперативную память. Перераспределение ничего не стоит. - person Hans Passant; 20.03.2014

Распределение стека (таким образом, выделение VLA) выполняется ОЧЕНЬ быстро, просто требуется быстрая модификация указателя стека (обычно это одна инструкция ЦП). Нет необходимости в дорогостоящем выделении/освобождении кучи.

Но почему бы просто не использовать вместо этого массив постоянного размера?

Предположим, вы пишете высокопроизводительный код и вам нужен буфер переменного размера, скажем, от 8 до 512 элементов. Вы можете просто объявить массив из 512 элементов, но если в большинстве случаев вам требуется только 8 элементов, то перераспределение может повлиять на производительность из-за влияния на расположение кеша в памяти стека. Теперь представьте, что эту функцию нужно вызывать миллионы раз.

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

person Will    schedule 05.01.2021
comment
Проблема с этими примерами в том, что они теоретические (и часто придуманы, чтобы преувеличить предполагаемую пользу функции). Например, какой высокопроизводительный код работает с переменными, но ограниченного размера данными и требует выделения массива? Какой рекурсивный алгоритм работает с массивами переменного размера, но при этом гарантирует отсутствие переполнения стека независимо от размера входных данных? - person Shahbaz; 23.01.2021