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

StackOverflow https://stackoverflow.com/questions/1018853

Вопрос

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

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

Это было полезно?

Решение

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

  

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

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

Другие советы

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

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

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

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

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

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

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

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

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)), затем функция предоставляет точку для операций очистки реализации (таких как закрытие файловых дескрипторов, освобождение внутренних указателей или ведение некоторого журнала) без явных изменений в клиентском коде:иногда это хорошая модель, которую нужно последовательно применять
    • в этом псевдо-OO стиле программирования естественно хотеть чего-то вроде WonderfulObject* p = WonderfulObject_AllocConstructor(); - это возможно, когда "конструктор" является функцией, возвращающей malloc-отредактированная память (поскольку память остается выделенной после того, как функция возвращает значение, которое будет сохранено в p), но не в том случае, если "конструктор" использует alloca
      • макрос-версия WonderfulObject_AllocConstructor можно было бы достичь этого, но "макросы - это зло" в том смысле, что они могут конфликтовать друг с другом и с кодом, отличным от макроса, и создавать непреднамеренные замены и, как следствие, труднодиагностируемые проблемы
    • пропавший без вести free операции могут быть обнаружены с помощью ValGrind, Purify и т.д.но отсутствующие вызовы "деструктора" вообще не всегда могут быть обнаружены - одно очень незначительное преимущество с точки зрения обеспечения соблюдения предполагаемого использования;некоторые alloca() реализации (такие как GCC) используют встроенный макрос для alloca(), таким образом, замена диагностической библиотеки использования памяти во время выполнения невозможна так, как это делается для malloc/realloc/free (например,электрический забор)
  • в некоторых реализациях есть тонкие проблемы:например, со страницы руководства 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(), достаточно мало, я думаю, что это хороший метод, который быстрее и удобнее, чем использование malloc() или иным образом.

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

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

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

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

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

распределять () это приятно и эффективно...но она также глубоко разрушена.

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

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

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

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

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

  

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

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

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

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

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

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

Но если вы решите использовать <=> ...

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

Вот почему:

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

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

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

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

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

 _ выделить блок в стеке

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

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

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

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

    Единственный недостаток alloca в том, что longjmp перематывает его назад.

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

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

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

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

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

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

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

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

    Обновить: Поскольку локальные массивы переменной длины были добавлены в C и C ++, и поскольку они представляют для компилятора очень похожие проблемы с генерацией кода в виде alloca, я вижу, что "редкость использования и сомнительный статус" не применимы к базовому механизму;но я бы все равно подозревал, что использование либо alloca, либо VLA имеет тенденцию ставить под угрозу генерацию кода в функции, которая их использует.Я был бы рад любым отзывам от разработчиков компилятора.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Массив и parray - это ТОЧНО одинаково с ТОЧНО одинаковыми рисками Сказать, что один лучше другого - это синтаксический, а не технический выбор.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Сравнить:

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

    с:

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

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top