По-прежнему не рекомендуется использовать alloca, почему?
Я не вижу такого консенсуса. Много сильных плюсов; несколько минусов:
- C99 предоставляет массивы переменной длины, которые часто используются преимущественно как обозначения, более совместимые с массивами фиксированной длины и интуитивно понятными в целом
- многие системы имеют меньше общей памяти / адресного пространства, доступного для стека, чем для кучи, что делает программу несколько более восприимчивой к исчерпанию памяти (через переполнение стека): это может рассматриваться как хорошая или плохая вещь - один из-за того, что стек автоматически не увеличивается, как это делает куча, состоит в том, чтобы предотвратить неконтролируемые программы, оказывающие столь же неблагоприятное влияние на всю машину
- при использовании в более локальной области действия (такой как
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
( например, электрический забор)- у некоторых реализаций есть тонкие проблемы: например, из справочной страницы Linux:
Во многих системах alloca () не может использоваться внутри списка аргументов вызова функции, потому что пространство стека, зарезервированное функцией alloca (), появилось бы в стеке в середине пространства для аргументов функции.
Я знаю, что этот вопрос помечен C, но как программист C ++ я подумал, что буду использовать C ++, чтобы проиллюстрировать потенциальную полезность
alloca
: приведенный ниже код (и здесь, в ideone ) создает вектор, отслеживающий полиморфные типы разного размера, которые выделяются в стеке (с временем жизни, связанным с возвратом функции), а не в куче.#include
#include #include 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 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 ::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 } }
Вне головы:
InputStream := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
OutputStream := THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE));
Попробуй ..