Почему использование alloca () не считается хорошей практикой? - PullRequest
364 голосов
/ 19 июня 2009

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

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

Ответы [ 25 ]

2 голосов
/ 14 сентября 2016

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

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

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

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

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

1 голос
/ 13 декабря 2016

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

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

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

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

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

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

1 голос
/ 25 июня 2014

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

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

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

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

0 голосов
/ 31 декабря 2018

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

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

0 голосов
/ 28 апреля 2017

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

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

Сравните:

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

с:

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

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

...