Почему использование alloca() не считается хорошей практикой?

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

Почему использование alloca() не рекомендуется, несмотря на вышеперечисленные особенности?

27 ответов

Решение

Ответ прямо в man страница (по крайней мере, в Linux):

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ Функция alloca() возвращает указатель на начало выделенного пространства. Если распределение вызывает переполнение стека, поведение программы не определено.

Что не означает, что его никогда не следует использовать. Один из проектов OSS, над которым я работаю, широко использует его, если вы не злоупотребляете им (allocaогромные ценности), это нормально. Как только вы пройдете отметку "несколько сотен байт", пришло время использовать malloc и друзья, вместо этого. Вы все еще можете получить ошибки выделения, но, по крайней мере, у вас будет некоторое указание на ошибку, а не просто выброс стека.

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

В заголовочном файле:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

В файле реализации:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

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

Итак, урок - не используйте alloca в функции, которые вы думаете, могут быть встроены.

Старый вопрос, но никто не упомянул, что его следует заменить массивами переменной длины.

char arr[size];

вместо

char *arr=alloca(size);

Он находится в стандарте C99 и существовал как расширение компилятора во многих компиляторах.

alloca() очень полезна, если вы не можете использовать стандартную локальную переменную, потому что ее размер должен был бы быть определен во время выполнения, и вы можете абсолютно гарантировать, что указатель, полученный из alloca(), НИКОГДА не будет использоваться после возврата этой функции.

Вы можете быть в полной безопасности, если вы

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

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

Как отмечается в этой публикации, есть несколько причин, почему alloca можно считать трудным и опасным:

  • Не все компиляторы поддерживают alloca,
  • Некоторые компиляторы интерпретируют предполагаемое поведение alloca иными словами, переносимость не гарантируется даже между компиляторами, которые ее поддерживают.
  • Некоторые реализации содержат ошибки.

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

все-таки использование alloca не рекомендуется, почему?

Я не воспринимаю такой консенсус. Много сильных плюсов; несколько минусов:

  • C99 предоставляет массивы переменной длины, которые часто используются преимущественно, поскольку обозначения более соответствуют массивам фиксированной длины и в целом интуитивно понятны
  • многие системы имеют меньше общей памяти / адресного пространства, доступного для стека, чем для кучи, что делает программу немного более восприимчивой к исчерпанию памяти (через переполнение стека): это может рассматриваться как хорошая или плохая вещь - один из-за того, что стек автоматически не растет так, как это делает куча, это предотвращение неконтролируемых программ, оказывающих столь же негативное влияние на всю машину
  • при использовании в более локальной области (например, while или же for цикл) или в нескольких областях, память накапливается за одну итерацию / область и не освобождается до выхода из функции: это контрастирует с обычными переменными, определенными в области структуры управления (например, for {int i = 0; i < 2; ++i) { X } будет накапливаться alloca в X запрашивается память, но память для массива фиксированного размера будет перерабатываться за одну итерацию).
  • современные компиляторы обычно не inline функции, которые вызывают alloca, но если вы заставите их, то alloca произойдет в контексте вызывающего (т. е. стек не будет освобожден, пока вызывающий не вернется)
  • давным давно alloca перешел от непереносимой функции / хака к стандартизированному расширению, но некоторое негативное восприятие может сохраняться
  • время жизни связано с областью действия функции, которая может или не может подойти программисту лучше, чем malloc явный контроль
  • необходимость использовать malloc побуждает задуматься об освобождении - если это происходит с помощью функции-оболочки (например, WonderfulObject_DestructorFree(ptr)), то функция обеспечивает точку для операций по очистке реализации (например, закрытие файловых дескрипторов, освобождение внутренних указателей или ведение журнала) без явных изменений в клиентском коде: иногда это хорошая модель для последовательного принятия
    • в этом псевдо-ОО стиле программирования естественно хотеть что-то вроде WonderfulObject* p = WonderfulObject_AllocConstructor(); - это возможно, когда "конструктор" - это функция, возвращающая malloc память (так как память остается выделенной после того, как функция возвращает значение, которое будет сохранено в p), но не в том случае, если "конструктор" использует alloca
      • Макро версия WonderfulObject_AllocConstructor мог бы достичь этого, но "макросы - это зло" в том смысле, что они могут конфликтовать друг с другом и не-макро-кодом и создавать непреднамеренные замены и последующие проблемы, которые трудно диагностировать
    • отсутствует free ValGrind, Purify и т. д. могут обнаруживать операции, но не всегда можно обнаружить пропущенные вызовы "деструктор" - одно очень незначительное преимущество с точки зрения принудительного использования по назначению; немного alloca() реализации (такие как GCC) используют встроенный макрос для alloca() Таким образом, замена во время выполнения библиотеки диагностики использования памяти невозможна malloc / realloc / free (например, электрический забор)
  • у некоторых реализаций есть тонкие проблемы: например, из man-страницы Linux:

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


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

#include <alloca.h>
#include <iostream>
#include <vector>

struct Base
{
    virtual ~Base() { }
    virtual int to_int() const = 0;
};

struct Integer : Base
{
    Integer(int n) : n_(n) { }
    int to_int() const { return n_; }
    int n_;
};

struct Double : Base
{
    Double(double n) : n_(n) { }
    int to_int() const { return -n_; }
    double n_;
};

inline Base* factory(double d) __attribute__((always_inline));

inline Base* factory(double d)
{
    if ((double)(int)d != d)
        return new (alloca(sizeof(Double))) Double(d);
    else
        return new (alloca(sizeof(Integer))) Integer(d);
}

int main()
{
    std::vector<Base*> numbers;
    numbers.push_back(factory(29.3));
    numbers.push_back(factory(29));
    numbers.push_back(factory(7.1));
    numbers.push_back(factory(2));
    numbers.push_back(factory(231.0));
    for (std::vector<Base*>::const_iterator i = numbers.begin();
         i != numbers.end(); ++i)
    {
        std::cout << *i << ' ' << (*i)->to_int() << '\n';
        (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                         // program depends on side effects of destructor
    }
}

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

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

Я нашел эту цитату в.... Хорошо, я сделал эту цитату. Но на самом деле, подумай об этом....

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

Я работал на настольных / серверных средах и встроенных системах. Многие встроенные системы вообще не используют кучу (они даже не ссылаются на ее поддержку), по причинам, которые включают в себя восприятие, что динамически распределенная память является злом из-за риска утечек памяти в приложении, которое никогда не будет когда-либо перезагружается годами или более разумным обоснованием того, что динамическая память опасна, потому что нельзя точно знать, что приложение никогда не фрагментирует свою кучу до точки ложного исчерпания памяти. Таким образом, у встроенных программистов остается мало альтернатив.

alloca() (или VLA) может быть просто правильным инструментом для работы.

Я снова и снова видел, как программист создает выделенный стеком буфер, "достаточно большой, чтобы справиться с любым возможным случаем". В глубоко вложенном дереве вызовов многократное использование этого (анти -?) Шаблона приводит к преувеличенному использованию стека. (Представьте себе дерево вызовов глубиной 20 уровней, где на каждом уровне по разным причинам функция слепо перераспределяет буфер в 1024 байта "просто для безопасности", когда обычно она использует только 16 или менее из них, и только в очень в редких случаях можно использовать больше.) Альтернативой является использование alloca() или VLA и выделять столько стекового пространства, сколько требуется вашей функции, чтобы избежать ненужной нагрузки на стек. Надеемся, что когда одна функция в дереве вызовов нуждается в выделении большего, чем обычно, другие в дереве вызова все еще используют свои обычные небольшие распределения, и общее использование стека приложения значительно меньше, чем если бы каждая функция слепо перераспределяла локальный буфер,

Но если вы решите использовать alloca()...

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

Я не думаю, что кто-то упомянул это: использование alloca в функции будет препятствовать или отключать некоторые оптимизации, которые могли бы быть применены в функции, так как компилятор не может знать размер стекового фрейма функции.

Например, общая оптимизация компиляторами C состоит в том, чтобы исключить использование указателя кадра внутри функции, вместо этого доступ к кадру осуществляется относительно указателя стека; так что есть еще один регистр для общего пользования. Но если в функции вызывается alloca, разница между sp и fp будет неизвестна для части функции, поэтому такая оптимизация не может быть выполнена.

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

Все остальные ответы верны. Однако, если вещь, которую вы хотите выделить с помощью alloca() достаточно маленький, я думаю, что это хорошая техника, которая быстрее и удобнее, чем использование malloc() или иным образом.

Другими словами, alloca( 0x00ffffff ) опасно и может вызвать переполнение, ровно столько, сколько char hugeArray[ 0x00ffffff ]; является. Будьте осторожны и разумны, и все будет в порядке.

Все уже указали на большую вещь, которая заключается в потенциальном неопределенном поведении из-за переполнения стека, но я должен отметить, что в среде Windows есть отличный механизм, чтобы уловить это, используя структурированные исключения (SEH) и защитные страницы. Поскольку стек увеличивается только по мере необходимости, эти защитные страницы находятся в областях, которые не выделены. Если вы выделяете их (переполняя стек), создается исключение.

Вы можете поймать это исключение SEH и вызвать _resetstkoflw, чтобы сбросить стек и продолжить свой веселый путь. Это не идеально, но это еще один механизм, по крайней мере, знать, что что-то пошло не так, когда вещи попадают в фанат. * У nix может быть что-то похожее, чего я не знаю.

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

Кроме того, помимо предотвращения утечек памяти, alloca не вызывает фрагментацию памяти, что очень важно. Я не думаю, что alloca - плохая практика, если вы используете ее разумно, что в основном верно для всего.:-)

Одна ловушка с alloca в том, что longjmp перематывает это

То есть, если вы сохраните контекст с setjmp, затем alloca немного памяти, тогда longjmp к контексту, вы можете потерять alloca память (без какого-либо уведомления). Указатель стека возвращается туда, где он был, и поэтому память больше не резервируется; если вы вызываете функцию или делаете другую allocaобрежешь оригинал alloca,

Чтобы уточнить, что я конкретно имею в виду здесь, это ситуация, в которой longjmp не возвращается из функции, где alloca состоялся! Скорее, функция сохраняет контекст с setjmp; затем выделяет память alloca и, наконец, longjmp имеет место в этом контексте. Эта функция alloca память не все свободна; просто вся память, которую он выделил с setjmp, Конечно, я говорю о наблюдаемом поведении; ни одно из таких требований не задокументировано alloca что я знаю.

Основное внимание в документации обычно уделяется концепции alloca память связана с активацией функции, а не с каким-либо блоком; что несколько вызовов alloca просто захватите больше стековой памяти, которая освобождается после завершения функции. Не так; память фактически связана с контекстом процедуры. Когда контекст восстановлен с longjmpтак же как и предыдущий alloca государство. Это является следствием того, что сам регистр указателя стека используется для выделения, а также (обязательно) сохраняется и восстанавливается в jmp_buf,

Между прочим, это, если это работает таким образом, обеспечивает вероятный механизм для преднамеренного освобождения памяти, которая была выделена с alloca,

Я столкнулся с этим как основной причиной ошибки.

Вот почему:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

Не то, чтобы кто-то написал этот код, но аргумент размера, который вы передаете alloca почти наверняка исходит от какого-то ввода, которое может быть злонамеренно направлено на то, чтобы ваша программа alloca что-то огромное подобное В конце концов, если размер не основан на вводе или не может быть большим, почему вы просто не объявили небольшой локальный буфер фиксированного размера?

Практически весь код, использующий alloca и / или C99 vlas имеет серьезные ошибки, которые приведут к сбоям (если вам повезет) или компрометации привилегий (если вам не повезет).

alloca () хороша и эффективна... но она также глубоко нарушена.

  • неправильное поведение области (область действия функции вместо области блока)
  • использование несовместимого с malloc (указатель наalloca () не должен освобождаться, поэтому вы должны отслеживать, откуда приходят ваши указатели на free (), только те, которые вы получили с помощью malloc ())
  • плохое поведение, когда вы также используете встраивание (область видимости иногда переходит к функции вызывающей стороны в зависимости от того, вставлен ли вызываемый объект или нет).
  • нет проверки границы стека
  • неопределенное поведение в случае сбоя (не возвращает NULL, как malloc... и что означает сбой, так как он все равно не проверяет границы стека...)
  • не анси стандарт

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

Если вам действительно нужно это C, вы можете использовать VLA (нет vla в C++, тоже неплохо). Они намного лучше, чем alloca (), в отношении поведения и согласованности области видимости. На мой взгляд, VLA - это своего рода правильно выполненная функция alloca ().

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

Если вы случайно напишите за пределами блока, выделенного alloca (например, из-за переполнения буфера), тогда вы перезапишете адрес возврата вашей функции, потому что он расположен "над" в стеке, то есть после выделенного блока.

Блок _alloca в стеке

Последствия этого двояки:

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

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

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

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

У процессов только ограниченный объем доступного стекового пространства - намного меньше, чем объем доступной памяти malloc(),

Используя alloca() Вы резко увеличиваете свои шансы получить ошибку переполнения стека (если вам повезет, или необъяснимый сбой, если нет).

К сожалению, действительно потрясающе alloca() отсутствует в почти удивительном TCC. GCC имеет alloca(),

  1. Он сеет семена своего собственного уничтожения. С возвращением в качестве деструктора.

  2. подобно malloc() он возвращает недопустимый указатель при сбое, который вызовет сбой в современных системах с MMU (и, надеюсь, перезапустит те, у кого нет).

  3. В отличие от автоматических переменных вы можете указать размер во время выполнения.

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

Если вы нажмете слишком глубоко, вы уверены в сегменте (если у вас есть MMU).

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

Использовать malloc() Я использую глобалы и присваиваю им NULL. Если указатель не NULL, я освобождаю его перед использованием malloc(),

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

3.2.5.2 Преимущества alloca

На самом деле, alloca не гарантирует использование стека. Действительно, реализация alloca в gcc-2.95 выделяет память из кучи, используя сам malloc. Кроме того, эта реализация содержит ошибки, это может привести к утечке памяти и неожиданному поведению, если вы вызовете ее внутри блока с дальнейшим использованием goto. Нет, чтобы сказать, что вы никогда не должны использовать его, но иногда иногда alloca приводит к большим накладным расходам, чем освобождает от фрома.

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

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

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}

На мой взгляд, alloca(), если он доступен, должен использоваться только ограниченным образом. Очень похоже на использование "goto", довольно большое число разумных в других отношениях людей сильно не любит использование, а также наличие alloca().

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

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

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

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

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

Почему никто не упоминает этот пример из документации GNU?

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

Предложить порядок чтения 1->2->3->1:

  1. https://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.htmlhttps://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.html
  2. Введение и детали из Нелокальные выходынелокальных выходов
  3. Пример Alloca

Я не думаю, что кто-то упомянул об этом, но у alloca также есть некоторые серьезные проблемы с безопасностью, которые не обязательно присутствуют в malloc (хотя эти проблемы также возникают с любыми массивами на основе стека, динамическими или нет). Поскольку память выделяется в стеке, переполнение / переполнение буфера имеет гораздо более серьезные последствия, чем просто malloc.

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

ИМО самый большой риск с массивами alloca и переменной длины заключается в том, что он может выйти из строя очень опасным образом, если размер выделения неожиданно велик.

Выделения в стеке обычно не требуют проверки в пользовательском коде.

Современные операционные системы обычно устанавливают страницу защиты ниже * для обнаружения переполнения стека. Когда стек переполняется, ядро ​​может либо расширить стек, либо остановить процесс. Linux расширил эту область защиты в 2017 году, чтобы она была значительно больше, чем страница, но по-прежнему имеет конечный размер.

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

* на наиболее распространенных сегодня системах стек растет вниз.

ИМХО, alloca считается плохой практикой, потому что все боятся исчерпать ограничение размера стека.

Я многому научился, читая эту ветку и некоторые другие ссылки:

Я использую alloca главным образом для того, чтобы сделать мои простые C-файлы компилируемыми в msvc и gcc без каких-либо изменений, стиля C89, без #ifdef _MSC_VER и т. Д.

Спасибо! Эта тема заставила меня зарегистрироваться на этом сайте:)

Функция alloca великолепна, и все скептики просто распространяют FUD.

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

Массив и парринг точно такие же, как и те же риски. Сказать, что один лучше другого - это синтаксический выбор, а не технический.

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

Почему это плохо?

Большинство ответов здесь в значительной степени упускают суть: есть причина, почему _alloca() потенциально хуже, чем просто хранить большие объекты в стеке.

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

Для сравнения:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

с:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

Проблема с последним должна быть очевидной.

Другие вопросы по тегам