Является ли alloca полностью заменимым?

Я читал довольно много мест, где alloca устарел и не должен использоваться, и вместо этого следует использовать массивы переменной длины.

Мой вопрос таков: можно ли полностью заменить alloca массивами переменной длины?

В моем конкретном случае у меня есть что-то вроде этого:

typedef struct { 
  int *value; 
  size_t size; 
  } some_type;

void SomeExternalFunction(some_type);

...

void foo(){
  //What I thought to do
  some_type bar;
  bar.value=alloca(sizeof(int)*10);
  SomeExternalFunction(bar);

  //what should be done without alloca
  some_type fizz;
  int tmp[10];
  fizz.value=tmp;
  SoemExternalFunction(fizz);
}

Я что-то упустил или это действительно хорошее использование alloca? Также предположим для этого примера, что по какой-то причине я хочу, чтобы значение было выделено в стеке


person Earlz    schedule 15.08.2010    source источник
comment
На самом деле это не массив переменной длины... Поскольку вы указали постоянную длину. И... Даже если бы это была была переменная длина, я на самом деле не вижу, что alloca покупает вам здесь, кроме как сделать цель кода немного более неясной. (Очевидно, что если вы используете компилятор, который не поддерживает массивы переменной длины, у вас все равно нет выбора между ними)   -  person Shog9    schedule 15.08.2010
comment
См. также: stackoverflow.com/questions/3452434 / (было бы дублирование на основе заголовка, но я чувствую, что вы действительно пытаетесь спросить что-то еще... хотя не могу сказать, что)   -  person Shog9    schedule 15.08.2010


Ответы (2)


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

typedef struct node { int data; struct node *next; };
void fun()
{
 struct node *n=0;
 int d;
 /* Now we are building a single-linked list on the stack! */
 while(d=get_something()) {
  struct node *x=alloca(sizeof(*x));
  x->next=n; 
  x->data=d;
  n=x;
 }
 do_something_with(n);
} // and the whole thing is deleted here..

Вы не можете сделать это с VLA.

person Nordic Mainframe    schedule 15.08.2010
comment
FWIW: это был бы отличный ответ как на , в каких случаях alloca() полезен? /a> и В чем разница между alloca(n) и char x[ п]? - person Shog9; 16.08.2010
comment
Это отличный ответ относительно разницы, но вы также можете отметить, что поведение alloca по существу определяется реализацией, поскольку оно не указано ни в одном (текущем) стандарте. - person R.. GitHub STOP HELPING ICE; 16.08.2010
comment
Кстати, есть способ сделать то же самое с VLA: сделать функцию рекурсивной и вызывать do_something_with(n) на самом глубоком уровне. :-) На самом деле у меня было реальное применение для этого, которое я еще не реализовал: облегченная реализация /lib/ld.so, которая выполняет динамическую компоновку в стеке, чтобы избежать накладных расходов malloc в крошечных программах, которые его не используют. В конце он раскрутит и отбросит все данные динамической компоновки тогда и только тогда, когда libdl не было связано; в противном случае он вызвал бы _start из самого глубокого уровня рекурсии. - person R.. GitHub STOP HELPING ICE; 16.08.2010
comment
@R: я помню, где-то читал, что были/являются alloca, которые освобождаются, когда текущий раздел фигурных скобок остается. Так что, вероятно, это хорошая идея, чтобы проверить руководство. - person Nordic Mainframe; 16.08.2010
comment
@R: Chicken Scheme использует аналогичную технику для выполнения своих распределений: программа скомпилирована в набор функций, в которых существуют продолжения вызова с хвостовым вызовом, что означает, что все распределение может происходить в стеке. Сборщик мусора будет перемещать живые объекты в кучу, когда стек заполнен, и перематывать стек, выполняя возврат. - person Nordic Mainframe; 16.08.2010

alloca полностью заменяется на malloc и free. Это немного больше работы, но если вы не очень осторожны, это важно. Почти весь код, использующий vla alloca или C99, уязвим для атак переполнения стека, и во многих реализациях они могут привести к повышению привилегий. Не существует переносимого способа узнать, насколько велик стек или сколько места в стеке осталось (или сколько накладных расходов сверх запрошенного размера может потребоваться для внутреннего использования компилятором или дальнейших вызовов функций), поэтому единственная разумная вещь, которую вы можете сделать, это сделать vla's/alloca безопасным накладывает чрезвычайно небольшие искусственные ограничения на размер данных, которые вы поддерживаете (например, несколько килобайт). На этом этапе вы могли бы также просто использовать простые автоматические объекты непеременной длины...

person R.. GitHub STOP HELPING ICE    schedule 16.08.2010
comment
Куча полностью заменяет стек. Неа. - person Edgehog.net; 07.09.2016
comment
В контексте реального времени важно использовать только стек и избегать динамической памяти (malloc/free/new), так как выделение памяти может остановить поток на несколько секунд, когда системе нужно выгрузить страницы на диск. - person diemo; 03.06.2019
comment
@diemo: ни одно из этих утверждений не соответствует действительности. Использование стека может привести к подкачке (если указатель стека переходит на новую страницу, а страница еще не создана, не была выгружена или отброшена), а использование выделенной (кучи) памяти не должно вызывать подкачка (например, если нет диска). - person R.. GitHub STOP HELPING ICE; 03.06.2019