Постарайтесь просто:
Элементы управления пользователя - это способ создания пользовательского, многоразового компонента. Пользовательский элемент управления может содержать другие элементы управления, но должен быть размещен в форме.
Формы Windows - это контейнер для элементов управления, включая пользовательские элементы управления. Хотя он содержит много похожих атрибутов, как пользовательский элемент управления, основной целью является управление элементами управления.
Ответ находится прямо на странице man
(по крайней мере, в Linux ):
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ Функция alloca () возвращает указатель на начало выделенное пространство. Если распределение вызывает переполнение стека, поведение программы не определено.
Что не означает, что его никогда не следует использовать. Один из проектов OSS, над которым я работаю, широко использует его, и если вы не злоупотребляете им (alloca
- это огромные ценности), это нормально. Как только вы пройдете отметку «несколько сотен байт», пришло время использовать malloc
и друзей. Вы все еще можете получить ошибки выделения, но, по крайней мере, у вас будет некоторое указание на ошибку, а не просто выброс стека.
ИМХО, alloca считается плохой практикой, потому что все боятся исчерпать ограничение размера стека.
Я многому научился, читая эту ветку и некоторые другие ссылки:
Я использую alloca в основном для мои простые C-файлы, скомпилированные в msvc и gcc без каких-либо изменений, стиль C89, отсутствие #ifdef _MSC_VER и т. д.
Спасибо! Эта тема заставила меня зарегистрироваться на этом сайте:)
Функция alloca великолепна, и все скептики просто распространяют FUD.
void foo()
{
int x = 50000;
char array[x];
char *parray = (char *)alloca(x);
}
Массив и parray точно такие же, с точно такими же рисками. Сказать, что один лучше другого - это синтаксический, а не технический выбор.
Что касается выбора переменных стека по сравнению с переменными кучи, есть много преимуществ для долгосрочных программ, использующих стек поверх кучи для переменных с продолжительностью жизни в области. Вы избегаете фрагментации кучи и можете избежать увеличения пространства процесса за счет использования неиспользуемого (непригодного) пространства кучи. Вам не нужно убирать это. Вы можете контролировать выделение стека в процессе.
Почему это плохо?
Я не думаю, что кто-то упоминал об этом, но у alloca также есть некоторые серьезные проблемы с безопасностью, которые не обязательно присутствуют в malloc (хотя эти проблемы также возникают с любыми массивами на основе стека, динамическими или нет). Так как память выделяется в стеке, переполнение / переполнение буфера имеет гораздо более серьезные последствия, чем просто malloc.
В частности, адрес возврата для функции хранится в стеке. Если это значение будет повреждено, ваш код может быть перемещен в любую исполняемую область памяти. Компиляторы идут на все, чтобы сделать это трудным (в частности, путем рандомизации размещения адресов). Тем не менее, это явно хуже, чем просто переполнение стека, так как лучший случай - SEGFAULT, если возвращаемое значение повреждено, но он также может начать выполнять случайный фрагмент памяти или в худшем случае некоторую область памяти, которая ставит под угрозу безопасность вашей программы. .
Не очень красиво, но если производительность действительно имеет значение, вы можете заранее выделить место в стеке.
Если вы уже установили максимальный размер блока памяти, который вам нужен, и хотите сохранить проверки переполнения, вы можете сделать что-то вроде:
void f()
{
char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
SomeType *p = (SomeType *)array;
(...)
}
Процессы имеют ограниченный объем доступного стекового пространства - намного меньше, чем объем памяти, доступный для malloc()
.
Используя alloca()
, вы резко увеличиваете свои шансы на получение ошибки переполнения стека (если вам повезет, или необъяснимого сбоя, если нет).
Большинство ответов здесь в значительной степени упускают суть: есть причина, почему использование _alloca()
потенциально хуже, чем простое хранение больших объектов в стеке.
Основное различие между автоматическим хранением и _alloca()
заключается в том, что последний страдает от дополнительной (серьезной) проблемы: выделенный блок не контролируется компилятором , поэтому компилятор не может оптимизировать или переработать его.
Сравните:
while (condition) {
char buffer[0x100]; // Chill.
/* ... */
}
с:
while (condition) {
char* buffer = _alloca(0x100); // Bad!
/* ... */
}
Проблема с последним должна быть очевидна.
Как отмечалось в в этой публикации группы новостей , существует несколько причин, по которым использование alloca
можно считать трудным и опасным:
alloca
. alloca
, поэтому переносимость не гарантируется даже между компиляторами, которые его поддерживают. На самом деле, alloca не гарантирует использование стека. Действительно, реализация alloca в gcc-2.95 выделяет память из кучи, используя сам malloc. Кроме того, эта реализация содержит ошибки, это может привести к утечке памяти и неожиданному поведению, если вы вызовете ее внутри блока с дальнейшим использованием goto. Нет, чтобы сказать, что вы никогда не должны его использовать, но иногда alloca приводит к большим накладным расходам, чем освобождает от фрома.
Старый вопрос, но никто не упомянул, что его следует заменить массивами переменной длины.
char arr[size];
вместо
char *arr=alloca(size);
Он находится в стандарте C99 и существовал как расширение компилятора во многих компиляторах.
Все остальные ответы верны. Однако, если вещь, которую вы хотите выделить с помощью alloca()
, достаточно мала, я думаю, что это хорошая техника, которая быстрее и удобнее, чем с помощью malloc()
или иным образом.
Другими словами, alloca( 0x00ffffff )
опасно и может вызвать переполнение, точно так же, как char hugeArray[ 0x00ffffff ];
. Будьте осторожны и разумны, и с вами все будет в порядке.
alloca () очень полезна, если вы не можете использовать стандартную локальную переменную, потому что ее размер должен быть определен во время выполнения, и вы можете абсолютно гарантировать, что указатель, полученный из alloca (), НИКОГДА не будет использоваться после этой функции возвращается .
Вы можете быть в полной безопасности, если вы
Реальная опасность исходит от вероятности того, что кто-то еще нарушит эти условия позже. Имея это в виду, он отлично подходит для передачи буферов функциям, которые форматируют текст в них:)
Одна проблема заключается в том, что он не является стандартным, хотя и широко поддерживается. При прочих равных условиях я бы всегда использовал стандартную функцию, а не обычное расширение компилятора.
К сожалению, действительно потрясающий alloca()
отсутствует в почти удивительном TCC. Gcc имеет alloca()
.
Он сеет семя собственного уничтожения. С возвращением в качестве деструктора.
Как и malloc()
, он возвращает недопустимый указатель при сбое, который вызовет сбой в современных системах с MMU (и, надеюсь, перезапустит те, у которых нет).
В отличие от автоматических переменных, вы можете указать размер во время выполнения.
Хорошо работает с рекурсией. Вы можете использовать статические переменные для достижения чего-то похожего на хвостовую рекурсию и использовать несколько других, передавая информацию на каждую итерацию.
Если вы нажмете слишком глубоко, вы уверены в сегментации (если у вас MMU).
Обратите внимание, что malloc()
не предлагает больше, так как возвращает NULL (что также приведет к segfault, если назначено), когда системе не хватает памяти. То есть все, что вы можете сделать, это внести залог или просто попытаться назначить его любым способом.
Для использования malloc()
я использую глобалы и присваиваю им NULL. Если указатель не равен NULL, я освобождаю его перед использованием malloc()
.
Вы также можете использовать realloc()
в качестве общего случая, если хотите скопировать любые существующие данные. Вам нужно проверить указатель, прежде чем выяснить, собираетесь ли вы копировать или объединять после realloc()
.
Все уже указали на большую вещь, которая заключается в потенциальном неопределенном поведении из-за переполнения стека, но я должен отметить, что в среде Windows есть отличный механизм, чтобы уловить это, используя структурированные исключения (SEH) и защитные страницы. Поскольку стек увеличивается только по мере необходимости, эти защитные страницы находятся в незанятых областях. Если вы выделяете их (переполняя стек), возникает исключение.
Вы можете поймать это исключение SEH и вызвать _resetstkoflw, чтобы сбросить стек и продолжить свой веселый путь. Это не идеально, но это еще один механизм, по крайней мере, знать, что что-то пошло не так, когда вещи попадают в фанат. * У nix может быть что-то похожее, чего я не знаю.
Я рекомендую ограничить ваш максимальный размер выделения, обернув alloca и отслеживая его внутри. Если бы вы были действительно хардкорны в этом, вы могли бы добавить некоторые области видимости в верхнюю часть вашей функции, чтобы отслеживать любые выделения alloca в области действия функции, и здравый смысл сравнивает это с максимальной суммой, допустимой для вашего проекта.
Кроме того, помимо предотвращения утечек памяти, alloca не вызывает фрагментацию памяти, что довольно важно. Я не думаю, что alloca - плохая практика, если вы используете ее разумно, что в принципе верно для всего. : -)
alloca () хороша и эффективна ... но она также глубоко сломана.
В большинстве случаев вы можете заменить его, используя локальные переменные и размер мажоранты. Если это используется для больших объектов, то поместить их в кучу обычно более безопасная идея.
Если вам действительно нужно это C, вы можете использовать VLA (нет vla в C ++, тоже неплохо). Они намного лучше, чем alloca () в отношении поведения и согласованности области видимости. Как я вижу, VLA являются своего рода alloca () , сделанными правильно.
Конечно, локальная структура или массив, использующий мажоранту необходимого пространства, все еще лучше, и если у вас нет такого мажорантного выделения кучи с использованием обычного malloc (), вероятно, это нормально. Я не вижу ни одного нормального варианта использования, где вам действительно нужно alloca () или VLA.
Место, где alloca()
особенно опасен, чем malloc()
, - это ядро - ядро типичной операционной системы имеет фиксированное пространство стека, жестко запрограммированное в одном из его заголовков; это не так гибко, как стек приложения. Выполнение вызова alloca()
с необоснованным размером может привести к сбою ядра. Некоторые компиляторы предупреждают использование alloca()
(и даже VLA в этом отношении) при определенных параметрах, которые должны быть включены при компиляции кода ядра - здесь лучше выделять память в куче, которая не фиксируется аппаратно кодированный предел.
Одна ловушка с alloca
заключается в том, что longjmp
перематывает его.
То есть, если вы сохраняете контекст с помощью setjmp
, затем alloca
некоторой памяти, а затем longjmp
в контексте, вы можете потерять alloca
память (без какого-либо уведомления). Указатель стека возвращается туда, где он был, и поэтому память больше не резервируется; если вы вызываете функцию или делаете другую alloca
, вы закроете оригинал alloca
.
Чтобы уточнить, что я здесь конкретно имею в виду, это ситуация, когда longjmp
не возвращается из функции, в которой имел место alloca
! Скорее функция сохраняет контекст с setjmp
; затем выделяет память с помощью alloca
и, наконец, longjmp имеет место для этого контекста. Память этой функции не полностью освобождена; просто вся память, которую он выделил со времен setjmp
. Конечно, я говорю о наблюдаемом поведении; ни одно из таких требований не задокументировано ни в одном alloca
, которое я знаю.
В документации основное внимание обычно уделяется концепции, согласно которой alloca
память связана с активацией функции , , а не с каким-либо блоком; что множественные вызовы alloca
просто захватывают больше стековой памяти, которая освобождается после завершения функции. Не так; память фактически связана с контекстом процедуры. Когда контекст восстанавливается с помощью longjmp
, то же происходит с предыдущим состоянием alloca
. Это является следствием того, что сам регистр указателя стека используется для выделения, а также (обязательно) сохраняется и восстанавливается в jmp_buf
.
Между прочим, это, если оно работает таким образом, обеспечивает правдоподобный механизм для преднамеренного освобождения памяти, которая была выделена с помощью alloca
.
Я столкнулся с этим как с основной причиной ошибки.
Я не думаю, что кто-то упоминал об этом: использование alloca в функции будет препятствовать или отключать некоторые оптимизации, которые могли бы быть применены в функции, так как компилятор не может знать размер фрейма стека функции.
Например, общая оптимизация компиляторами C заключается в том, чтобы исключить использование указателя кадра внутри функции, вместо этого осуществляется доступ к кадру относительно указателя стека; так что есть еще один регистр для общего пользования. Но если в функции вызывается alloca, разница между sp и fp будет неизвестна для части функции, поэтому такая оптимизация не может быть выполнена.
Учитывая редкость его использования и его теневое состояние в качестве стандартной функции, разработчики компилятора вполне могут отключить любую оптимизацию, чтобы могла вызвать проблемы с alloca, если бы потребовалось больше, чем немного усилий, чтобы заставить его работать с alloca.
ОБНОВЛЕНИЕ: Так как локальные массивы переменной длины были добавлены в C и C ++, и так как они представляют очень похожие проблемы генерации кода для компилятора как alloca, я вижу, что «редкость статус использования и тенистости »не относится к основному механизму; но я все еще подозреваю, что использование alloca или VLA ведет к компрометации процесса генерации кода внутри функции, которая их использует. Буду рад любым отзывам дизайнеров компиляторов.
По моему мнению, alloca (), если он доступен, должен использоваться только ограниченным образом. Очень похоже на использование «goto», довольно большое количество разумных в остальном людей сильно отвращается не только к использованию alloca (), но и к его существованию.
Для встроенного использования, где размер стека известен, и ограничения могут быть наложены с помощью соглашения и анализа размера распределения, и где компилятор не может быть обновлен для поддержки C99 +, использование alloca () хорошо, и я Известно, что использовать его.
Когда это возможно, VLA могут иметь некоторые преимущества по сравнению с alloca (): компилятор может генерировать проверки ограничения стека, которые будут перехватывать доступ за пределами границ при использовании доступа в стиле массива (я не знаю, делают ли это какие-либо компиляторы, но это может быть сделано), и анализ кода может определить, правильно ли ограничены выражения доступа к массиву. Обратите внимание, что в некоторых средах программирования, таких как автомобильное, медицинское оборудование и авионика, этот анализ должен выполняться даже для массивов фиксированного размера, как автоматического (в стеке), так и статического распределения (глобального или локального).
На архитектурах, которые хранят как данные, так и адреса возврата / указатели фреймов в стеке (насколько я знаю, это все из них), любая переменная, выделенная в стеке, может быть опасной, поскольку адрес переменной может быть получен, а вход не проверен. значения могут допускать всевозможные неприятности.
Переносимость менее важна во встроенном пространстве, однако это хороший аргумент против использования alloca () вне тщательно контролируемых обстоятельств.
За пределами встроенного пространства для эффективности я использовал alloca () в основном внутри функций ведения журнала и форматирования, а также в нерекурсивном лексическом сканере, где временные структуры (выделяемые с помощью alloca () создаются во время токенизации и классификации, затем постоянный объект (выделенный через malloc ()) заполняется до возврата функции. Использование alloca () для более мелких временных структур значительно уменьшает фрагментацию при выделении постоянного объекта.
По-прежнему не рекомендуется использовать alloca, почему?
Я не вижу такого консенсуса. Много сильных плюсов; несколько минусов:
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();
- это возможно, когда onstructor "- это функция, возвращающая память malloc
(так как память остается выделенной после того, как функция возвращает значение, которое будет сохранено в p
), но не в том случае, если« конструктор »использует alloca
WonderfulObject_AllocConstructor
могла бы достичь этого, но «макросы - это зло» в том смысле, что они могут конфликтовать друг с другом и не-макрокодом и создавать непреднамеренные замены и вытекающие из этого проблемы, которые трудно диагностировать li> ul> free
операции могут быть обнаружены ValGrind, Purify и т. Д., Но пропущенные вызовы «деструктора» не всегда могут быть обнаружены вообще - одно очень незначительное преимущество с точки зрения принудительного применения по назначению; некоторые реализации alloca()
(такие как GCC) используют встроенный макрос для alloca()
, поэтому подстановка диагностической библиотеки использования памяти во время выполнения невозможна, как для malloc
/ realloc
/ free
( например, электрический забор) Во многих системах 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) может быть просто подходящим инструментом для работы. Я видел время & amp; снова, когда программист делает выделенный стеком буфер, «достаточно большой, чтобы справиться с любым возможным случаем». В глубоко вложенном дереве вызовов повторное использование этого (анти -?) Шаблона приводит к чрезмерному использованию стека. (Представьте себе дерево вызовов глубиной 20 уровней, где на каждом уровне по разным причинам функция слепо перераспределяет буфер в 1024 байта «просто для безопасности», когда обычно она использует только 16 или менее из них, и только в очень в редких случаях может использоваться больше.) Альтернатива - использовать alloca()
или VLA и выделять только столько стекового пространства, сколько требуется вашей функции, чтобы избежать ненужной нагрузки на стек. Надеемся, что когда одна функция в дереве вызовов нуждается в распределении, превышающем нормальное, другие в дереве вызова все еще используют свои обычные небольшие выделения, и общее использование стека приложения значительно меньше, чем если бы каждая функция слепо перераспределяла локальный буфер .
alloca()
... Судя по другим ответам на этой странице, кажется, что VLA должны быть безопасными (они не объединяют распределения стека при вызове из цикла), но если вы используете alloca()
, будьте осторожны, чтобы не использовать его внутри цикла, и сделайте уверенным, что ваша функция не может быть встроенной, если есть вероятность, что она может быть вызвана в цикле другой функции.
Одна из самых запоминающихся ошибок, которые у меня были, была связана со встроенной функцией, которая использовала alloca
. Это проявилось как переполнение стека (потому что оно размещается в стеке) в случайных точках выполнения программы.
В заголовочном файле:
void DoSomething() {
wchar_t* pStr = alloca(100);
//......
}
В файле реализации:
void Process() {
for (i = 0; i < 1000000; i++) {
DoSomething();
}
}
Итак, что случилось, это была встроенная функция компилятора DoSomething
, и все выделения стека происходили внутри Process()
функции и, таким образом, взорвать стек. В мою защиту (и я не был тем, кто обнаружил проблему; мне пришлось пойти и поплакаться с одним из старших разработчиков, когда я не мог это исправить), это было не прямо alloca
, это было одно из Макросы преобразования строк ATL.
Итак, урок - не используйте alloca
в функциях, которые, по вашему мнению, могут быть встроенными.
Если вы случайно записываете за пределы блока, выделенного с помощью alloca
(например, из-за переполнения буфера), то вы перезапишете адрес возврата вашей функции, потому что тот расположен «выше» в стеке, то есть после вашего выделенного блока.
Следствием этого является двоякое:
Программа будет впечатляюще сбоить, и это будет невозможно сказать, почему или где произошел сбой (стек, скорее всего, будет разматываться на случайный адрес из-за перезаписанного указателя кадра).
Это делает переполнение буфера во много раз более опасным, поскольку злонамеренный пользователь может создать специальную полезную нагрузку, которая будет помещена в стек и, следовательно, может быть выполнена.
Напротив, если вы пишете за пределами блока в куче, вы «просто» получаете повреждение в куче. Программа, вероятно, неожиданно завершит работу, но правильно размотает стек, тем самым уменьшая вероятность выполнения вредоносного кода.
И вот почему:
char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;
Не то, чтобы кто-то писал этот код, но аргумент размера, который вы передаете alloca
, почти наверняка исходит из какого-то ввода, которое может быть злонамеренно направлено на получение ваша программа для alloca
чего-то такого огромного. В конце концов, если размер не основан на вводе или не может быть большим, почему вы просто не объявили небольшой локальный буфер фиксированного размера?
Практически весь код, использующий alloca
и / или C99 vlas имеет серьезные ошибки, которые приведут к сбоям (если вам повезет) или компрометации привилегий (если вам не повезет).