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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

char arr[size];

вместо

char *arr=alloca(size);

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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

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


Я знаю, что этот вопрос отмечен 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
    }
}

Ответ 8

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

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

Ответ 9

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

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

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

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

Ответ 10

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

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

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

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

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

Ответ 11

Вот почему:

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

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

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

Ответ 12

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

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

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

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

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

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

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

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

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

Ответ 13

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

Ответ 14

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

_ alloca block on the stack

Следствием этого является двукратное:

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

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

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

Ответ 15

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

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

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

ОБНОВЛЕНИЕ: Так как локальные массивы переменной длины были добавлены в C и C++, и так как они представляют очень похожие проблемы генерации кода для компилятора, как alloca, я вижу, что "редкость использования и теневое состояние" не относится к основной механизм; но я все еще подозреваю, что использование alloca или VLA ведет к компрометации процесса генерации кода внутри функции, которая их использует. Буду рад любым отзывам от разработчиков компиляторов.

Ответ 16

К сожалению, действительно удивительный alloca() отсутствует в почти потрясающей tcc. У Gcc есть alloca().

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

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

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

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

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

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

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

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

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

Ответ 17

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

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

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

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

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

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

Ответ 18

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

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

Ответ 19

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

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

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

    (...)
}

Ответ 20

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

Ответ 21

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

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

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

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

Ответ 22

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

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

Массив и парсер являются ТОЧНО одинаковыми с ТОЧНО теми же рисками. Говорить один лучше другого - это синтаксический выбор, а не технический.

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

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

Ответ 23

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

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

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

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

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

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

Ответ 24

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

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

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

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

с:

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

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

Ответ 25

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

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