it-roy-ru.com

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

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

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

347
Vaibhav

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

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

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

206
Sean Bright

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

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

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

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

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

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

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

181
Igor Zevaka

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

char arr[size];

вместо

char *arr=alloca(size);

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

65
Patrick Schlüter

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

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

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

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

53
Arthur Ulfeldt

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

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

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

25
David Thornley

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

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

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

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


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

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

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

12
JSBձոգչ

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

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

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

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

11
SilentDirge

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

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

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

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

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

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

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

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

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

9
phonetagger

Вот почему:

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

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

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

9
R..

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

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

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

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

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

9
kriss

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

7
Sondhi Chakraborty

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

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

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

4
greggo

Одна ошибка с alloca в том, что longjmp перематывает его.

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

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

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

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

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

4
Kaz

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

 _alloca block on the stack

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

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

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

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

4
rustyx

К сожалению, по-настоящему потрясающая функция alloca() отсутствует в почти удивительном tcc. Gcc имеет alloca().

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

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

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

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

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

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

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

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

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

4
zagam

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

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

3
RichieHindle

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

2
user7491277

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

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

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

    (...)
}
2
Sylvain Rodrigue

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

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

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

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

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

1
mlwmohawk

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

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

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

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

1
ytoto

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

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

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

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

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

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

1
Daniel Glasser

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

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

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

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

с:

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

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

0
alecov

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

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

0
Dyllon Gagnier