В чем смысл __builtin_alloca [duplicate]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
331
задан Mohit Kanwar 25 August 2015 в 05:51
поделиться

25 ответов

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

RETURN VALUE Функция alloca () возвращает указатель на начало выделенного пространства. Если распределение вызывает переполнение стека, поведение программы не определено.

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

193
ответ дан Sean Bright 18 August 2018 в 05:29
поделиться
  • 1
    Значит, нет проблем с тем, что у вас также не было бы объявление больших массивов? – T.E.D. 19 June 2009 в 17:32
  • 2
    @Sean: Да, риск переполнения стека является причиной, но эта причина немного глупо. Во-первых, потому что (как говорит Вайбхав) большие локальные массивы вызывают точно такую ​​же проблему, но не настолько, насколько они поношены. Кроме того, рекурсия может так же легко взорвать стек. Извините, но я надеюсь, что вы с надеждой справитесь с преобладающей идеей о том, что причина, указанная на странице руководства, оправдана. – j_random_hacker 27 June 2010 в 14:22
  • 3
    Я имею в виду, что оправдание, данное на странице руководства, не имеет смысла, поскольку alloca () точно так же «плохо», как другие (локальные массивы или рекурсивные функции), которые считаются кошерными. – j_random_hacker 30 June 2010 в 02:44
  • 4
    @ninjalj: Не очень опытные программисты на C / C ++, но я думаю, что многие люди, которые боятся alloca(), не испытывают такого же страха перед локальными массивами или рекурсией (на самом деле многие люди, которые будут кричать alloca(), будут хвалить рекурсию, потому что он «выглядит элегантно»). Я согласен с советом Шона (& quot; alloca () отлично подходит для небольших распределений & quot;), но я не согласен с мнением, что рамки alloca () как однозначно злые среди 3 - они одинаково опасны! – j_random_hacker 2 August 2010 в 06:33
  • 5
    Примечание. Учитывая «оптимистичный» Linux, стратегия распределения памяти, вы, скорее всего, не будете получать какие-либо указания на ошибку исчерпания кучи ... вместо этого malloc () вернет вам хороший указатель, отличный от NULL, а затем, когда вы попытаетесь на самом деле доступ к адресному пространству, на который он указывает, ваш процесс (или какой-либо другой процесс, непредсказуемо) будет убит OOM-killer. Конечно, это «особенность». Linux, а не как проблема C / C ++, но это нужно иметь в виду при обсуждении того, является ли alloca () или malloc () «более безопасным». :) – Jeremy Friesner 28 July 2013 в 03:53

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

  • Не все компиляторы поддерживают alloca.
  • Некоторые компиляторы по-разному интерпретируют предполагаемое поведение alloca, поэтому переносимость не гарантируется даже между компиляторами, которые его поддерживают.
  • Некоторые реализации ошибочны.
37
ответ дан A. Wilcox 18 August 2018 в 05:29
поделиться
  • 1
    Одна вещь, которую я видел в этой ссылке, которая не находится в другом месте на этой странице, состоит в том, что для функции, использующей alloca(), требуются отдельные регистры для хранения указателя стека и указателя кадра. На процессорах x86 & gt; = 386 указатель стека ESP может использоваться для обоих, освобождая EBP - если не используется alloca(). – j_random_hacker 27 June 2010 в 14:30
  • 2
    Другим хорошим моментом на этой странице является то, что, если генератор кода компилятора не обрабатывает его как особый случай, f(42, alloca(10), 43); может потерпеть крах из-за возможности того, что указатель стека будет отрегулирован на alloca() после по меньшей мере одним из аргументы нажимаются на него. – j_random_hacker 27 June 2010 в 14:38
  • 3
    Связанный пост, похоже, написан Джоном Левином - чуваком, который написал «Linkers and Loaders», я бы доверял всему, что он говорит. – user318904 13 August 2011 в 07:06
  • 4
    +1 за то, что вы не делаете нерелевантных аргументов, таких как ответы с гораздо более высоким голосом, которые забывают, что их аргументы могут быть применены также к массивам переменной длины или любым устройствам, использующим стеки. – einpoklum 17 July 2015 в 21:00
  • 5

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

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

Сравнить:

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

с:

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

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

-1
ответ дан alecov 18 August 2018 в 05:29
поделиться
  • 1
    Есть ли у вас практические примеры, демонстрирующие разницу между VLA и alloca (да, я говорю VLA, потому что alloca больше, чем просто создатель массивов статического размера)? – Ruslan 29 April 2017 в 15:33
  • 2
    Есть варианты использования второго, который первый не поддерживает. Я могу захотеть иметь записи «n» после того, как цикл будет выполнен с «n» раз - возможно, в связанном списке или дереве; эта структура данных затем удаляется, когда функция в конечном итоге возвращается. Который не сказать, что я бы закодировал что-нибудь таким образом :-) – greggo 18 January 2018 в 20:20
  • 3
    И я бы сказал, что «компилятор не может его контролировать». потому что именно так определяется alloca (); современные компиляторы знают, что такое alloca, и рассматривают его специально; это не просто функция библиотеки, как в 80-е годы. C99 VLAs в основном alloca с областью блока (и более качественная типизация). Не более или менее контроль, просто соответствующий разной семантике. – greggo 18 January 2018 в 20:28
  • 4
    @greggo: Если вы - спутник, я с удовольствием услышу, почему вы считаете, что мой ответ не полезен. – alecov 18 January 2018 в 21:16
  • 5
    Не спустил вниз. комментарии выше стоят, хотя – greggo 9 February 2018 в 18:27

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

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

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

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

0
ответ дан Community 18 August 2018 в 05:29
поделиться
  • 1
    Имейте в виду, что нет такой вещи, как «нить». на этом сайте. Stack Overflow имеет формат вопрос-ответ, а не формат обсуждения. & Quot; ответ & Quot; не похоже на «Reply & quot; на дискуссионном форуме; это означает, что вы фактически предоставляете ответ на вопрос и не должны использоваться для ответа на другие ответы или комментариев по этой теме. Если у вас есть хотя бы 50 rep, вы можете опубликовать комментарии , но обязательно прочтите «Когда не должен комментировать? & Quot; раздел. Пожалуйста, прочитайте страницу About , чтобы лучше понять формат сайта. – Adi Inbar 24 June 2014 в 22:35

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

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

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

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

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

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

0
ответ дан Daniel Glasser 18 August 2018 в 05:29
поделиться

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

25
ответ дан David Thornley 18 August 2018 в 05:29
поделиться

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

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

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

4
ответ дан greggo 18 August 2018 в 05:29
поделиться

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

В файле заголовка:

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

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

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

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

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

169
ответ дан Igor Zevaka 18 August 2018 в 05:29
поделиться
  • 1
    Интересно. Но разве это не было бы ошибкой компилятора? В конце концов, вложение изменило поведение кода (оно задерживало освобождение пространства, выделенного с помощью alloca). – sleske 17 October 2010 в 19:55
  • 2
    По-видимому, по крайней мере GCC учтет это: «Обратите внимание, что определенные способы использования в определении функции могут сделать его непригодным для встроенной подстановки. Среди этих обычаев: использование varargs, использование alloca, [...] ". gcc.gnu.org/onlinedocs/gcc/Inline.html – sleske 17 October 2010 в 19:57
  • 3
    Какой компилятор вы курили? – Thomas Eding 16 November 2011 в 23:34
  • 4
    Извиняюсь, я собирался в режиме отладки! VS2010 дает: предупреждение C4750: 'bool __cdecl inlined_TestW32Mem5 (void)': функция с _alloca (), встроенная в цикл – Benj 15 March 2012 в 15:40
  • 5
    Я не понимаю, почему компилятор не использует область видимости для определения того, что allocas в подкасте более или менее «освобождены»: указатель стека может вернуться к своей точке перед входом в область, например, что сделано при возврате из функции (не так ли?) – moala 16 May 2012 в 13:18

К сожалению, действительно удивительный alloca() отсутствует из почти удивительного tcc. Gcc имеет alloca().

  1. Он сеет семя своего собственного уничтожения. С возвратом в качестве деструктора.
  2. Как и malloc(), он возвращает недопустимый указатель на fail, который будет segfault на современных системах с MMU (и, надеюсь, перезапустить их без).
  3. В отличие от авто переменные вы можете указать размер во время выполнения.

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

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

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

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

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

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

49
ответ дан Jonathan Leffler 18 August 2018 в 05:29
поделиться
  • 1
    Функция VLA (переменная длина массива) C99 поддерживает локальные переменные динамического размера без явного требования использования alloca (). – Jonathan Leffler 23 June 2009 в 00:57
  • 2
    Neato! нашел дополнительную информацию в разделе «3.4 Variable Length Array» programersheaven.com/2/Pointers-and-Arrays-page-2 – Arthur Ulfeldt 23 June 2009 в 01:15
  • 3
    Но это не отличается от обработки указателями на локальные переменные. Их тоже можно обмануть ... – glglgl 18 October 2012 в 08:28
  • 4
    @Jonathan Leffler - одна вещь, которую вы можете сделать с помощью alloca, но вы не можете делать с VLA, используя с ними ключевое слово ограничения. Например: float * ограничивать heavyly_used_arr = alloca (sizeof (float) * size); вместо float heavyly_used_arr [size]. Это может помочь некоторым компиляторам (gcc 4.8 в моем случае) оптимизировать сборку, даже если размер является константой компиляции. См. Мой вопрос об этом: stackoverflow.com/questions/19026643/using-restrict-with-arrays – Piotr Lopusiewicz 2 October 2013 в 00:29
  • 5
    @JonathanLeffler VLA является локальным для блока, который содержит его. С другой стороны, alloca() выделяет память, которая длится до конца функции. Это означает, что, по-видимому, нет простого, удобного перевода в VLA f() { char *p; if (c) { /* compute x */ p = alloca(x); } else { p = 0; } /* use p */ }. Если вы считаете, что можно автоматически перевести использование alloca в использование VLA, но для описания того, как это сделать, требуется больше, чем комментарий, я могу задать вопрос. – Pascal Cuoq 24 March 2014 в 11:40
  • 6
    Фактически спецификация alloca не говорит, что возвращает недопустимый указатель на fail (переполнение стека), он говорит, что имеет неопределенное поведение ... и для malloc он говорит, что возвращает NULL, а не случайный недопустимый указатель (в порядке, оптимизация Linux с оптимизацией памяти делает это бесполезный). – kriss 2 September 2014 в 11:04
  • 7
    @kriss Linux может убить ваш процесс, но, по крайней мере, он не рискует неопределенным поведением – craig65535 6 October 2017 в 07:25
  • 8
  • 9
    Я пытался сказать, что malloc, возвращающий NULL, или в случае Linux, доступ к памяти, убивающий ваш процесс, предпочтительнее неопределенного поведения alloca. Я думаю, что, должно быть, неправильно написал ваш первый комментарий. – craig65535 7 October 2017 в 18:19

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

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

11
ответ дан JSBձոգչ 18 August 2018 в 05:29
поделиться

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

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

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

Это нигде не документировано; конечно, описание ISO C setjmp ничего не говорит о взаимодействиях с alloca, так как не описывает такую ​​функцию. Реализации, которые предоставляют alloca, также не документируют это.

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

Я столкнулся с этим, что я знаю.

4
ответ дан Kaz 18 August 2018 в 05:29
поделиться

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

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

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

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

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

9
ответ дан kriss 18 August 2018 в 05:29
поделиться
  • 1
    Я не вижу причины для downvote (без комментариев, кстати) – gd1 25 February 2015 в 19:39
  • 2
    Только имена имеют область видимости. alloca не создает имя, а только диапазон памяти, который имеет время жизни . – curiousguy 27 October 2015 в 07:27
  • 3
    @curiousguy: вы просто играете со словами. Для автоматических переменных я мог бы также говорить о времени жизни базовой памяти, поскольку она соответствует области имени. В любом случае проблема заключается не в том, как мы ее называем, а в нестабильности жизни / объема памяти, возвращаемой alloca и исключительном поведении. – kriss 27 October 2015 в 09:53
  • 4
    Я хотел бы, чтобы у alloca была соответствующая «freea», со спецификацией, вызывающей «freea». будет отменять эффекты "alloca" который создал объект и все последующие, и требование, чтобы хранилище «распределялось» внутри fucntion должно быть «свободно» в нем. Это позволило бы почти всем реализациям поддерживать alloca / freea совместимым образом, облегчило бы проблемы вложения и вообще делало вещи намного более чистыми. – supercat 31 March 2016 в 22:15
  • 5
    @supercat - Я тоже так хочу. По этой причине (и более) я использую уровень абстракции (в основном макросы и встроенные функции), поэтому я никогда не называю alloca или malloc или free напрямую. Я говорю такие вещи, как {stack|heap}_alloc_{bytes,items,struct,varstruct} и {stack|heap}_dealloc. Таким образом, heap_dealloc просто вызывает free, а stack_dealloc - нет-op. Таким образом, распределение стека может быть легко понижено до распределения кучи, и намерения также более ясны. – Todd Lehman 1 April 2016 в 15:09

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

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

Массив и парсер являются ТОЧНО одинаковыми с ТОЧНО теми же рисками. Говорить одно лучше другого - это синтаксический выбор, а не технический.

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

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

1
ответ дан mlwmohawk 18 August 2018 в 05:29
поделиться

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

7
ответ дан Nubok 18 August 2018 в 05:29
поделиться
  • 1
    alloca() не более опасен, чем int foo[bar];, где bar - некоторое произвольное целое число. – Todd Lehman 25 August 2015 в 23:20

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

char arr[size];

вместо

char *arr=alloca(size);

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

56
ответ дан Patrick Schlüter 18 August 2018 в 05:29
поделиться
  • 1
    Он упоминается Джонатаном Леффлером в ответ на ответ Артура Ульфельдта. – ninjalj 1 August 2010 в 23:31
  • 2
    Действительно, но он показывает также, как легко его пропустить, поскольку я не видел его, несмотря на чтение всех ответов перед публикацией. – Patrick Schlüter 2 August 2010 в 10:50
  • 3
    Если только MSVC2010 поддерживает C99 ... – Benj 15 March 2012 в 15:56
  • 4
    Одна заметка - это массивы переменной длины, а не динамические массивы. Последние могут быть изменены и обычно выполняются в куче. – Tim Čas 9 December 2012 в 18:42
  • 5
    Visual Studio 2015, скомпилировав некоторые C ++, имеет ту же проблему. – ahcox 7 April 2016 в 15:35

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

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

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

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

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

alloca() (или VLAs) может быть правильным инструментом для задания.

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

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

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

9
ответ дан phonetagger 18 August 2018 в 05:29
поделиться
  • 1
    Твердый вопрос об избежании повторных наихудших распределений .... – Tony Delroy 1 April 2016 в 04:00
  • 2
    Я согласен с этим. Опасно alloca() истинно, но то же самое можно сказать и о утечках памяти с помощью malloc() (почему бы не использовать GC тогда? Можно утверждать). alloca() при использовании с осторожностью может быть действительно полезно уменьшить размер стека. – Felipe Tonello 2 February 2017 в 16:44
  • 3

Вот почему:

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

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

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

9
ответ дан R.. 18 August 2018 в 05:29
поделиться
  • 1
    Downvoter, следите за комментариями? – R.. 17 May 2011 в 13:44
  • 2
    Мир, возможно, никогда не узнает. :( Сказав это, я надеюсь, что вы можете уточнить вопрос, который у меня есть о alloca. Вы сказали, что почти весь код, который использует его, имеет ошибку, но я планировал его использовать, я обычно проигнорировал бы такой иск , но исходящий от вас я не буду. Я пишу виртуальную машину, и я бы хотел выделить переменные, которые не выходят из функции в стеке, а не динамически из-за огромного ускорения. есть альтернативный подход, который имеет те же характеристики производительности? Я знаю, что могу приблизиться к пулам памяти, но это все еще не так дешево. Что бы вы сделали? – GManNickG 26 May 2011 в 03:33
  • 3
    Знать, что тоже опасно? Это: *0 = 9; УДИВИТЕЛЬНО !!! Думаю, я никогда не должен использовать указатели (или, по крайней мере, разыгрывать их). Эрр, подождите. Я могу проверить, является ли оно нулевым. Хм. Думаю, я также могу проверить размер памяти, которую я хочу выделить через alloca. Странный человек. Weird. – Thomas Eding 16 November 2011 в 23:41
  • 4
    *0=9; недействительно C. Что касается проверки размера, который вы переходите на alloca, проверьте его на что? Невозможно узнать предел, и если вы собираетесь протестировать его с небольшим фиксированным известным безопасным размером (например, 8k), вы можете просто использовать массив фиксированного размера в стеке. – R.. 17 November 2011 в 05:07
  • 5
    «Проблема с вашим» или размером, как известно, достаточно мала или зависит от входа и, следовательно, может быть сколь угодно большой ». как я вижу, это так же сильно, как и рекурсия. Практический компромисс (для обоих случаев) состоит в том, чтобы предположить, что если размер ограничен small_constant * log(user_input), то у нас, вероятно, будет достаточно памяти. – j_random_hacker 16 April 2012 в 04:00

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

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

3
ответ дан RichieHindle 18 August 2018 в 05:29
поделиться

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

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

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

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

4
ответ дан rustyx 18 August 2018 в 05:29
поделиться
  • 1
    Ничто в этой ситуации не сильно отличается от опасностей переполнения буфера буфером с фиксированным размером стека. Эта опасность не уникальна для alloca. – phonetagger 31 March 2016 в 21:15
  • 2
    Конечно нет. Но проверьте исходный вопрос. Возникает вопрос: какова опасность alloca по сравнению с malloc (таким образом, буфер не фиксированного размера в стеке). – rustyx 1 April 2016 в 11:29

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

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

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

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

11
ответ дан SilentDirge 18 August 2018 в 05:29
поделиться

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

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

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

    (...)
}
2
ответ дан Sylvain Rodrigue 18 August 2018 в 05:29
поделиться

still alloca use не рекомендуется, почему?

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

  • 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), но не при использовании «конструктора» s 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
        }
    }
    
    21
    ответ дан Tony Delroy 18 August 2018 в 05:29
    поделиться
    • 1
      +1 для упоминания VLA C99 – Justin Meiners 26 August 2013 в 23:58
    • 2
      нет +1 из-за рыбного идиосинкратического способа обработки нескольких типов :-( – einpoklum 17 July 2015 в 21:02
    • 3
      @einpoklum: хорошо, что глубоко просветляет ... спасибо. – Tony Delroy 18 July 2015 в 05:25
    • 4
      Позвольте мне перефразировать: Это очень хороший ответ. До того момента, когда я думаю, вы предлагаете, чтобы люди использовали своего рода контр-шаблон. – einpoklum 18 July 2015 в 09:36
    • 5
      Что касается примера, меня обычно беспокоит то, что требует always_inline, чтобы избежать повреждения памяти .... – greggo 2 May 2017 в 17:33

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

    2
    ответ дан user7491277 18 August 2018 в 05:29
    поделиться
    49
    ответ дан Jonathan Leffler 6 September 2018 в 19:37
    поделиться
    49
    ответ дан Jonathan Leffler 30 October 2018 в 01:04
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: