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
), но не при использовании «конструктора» salloca
макро-версияWonderfulObject_AllocConstructor
могла бы достичь этого, но «макросы злы» в том, что они могут конфликтовать друг с другом и не-макрокодом и создавать непреднамеренные замены и, следовательно, трудно диагностировать недостающие проблемы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 } }
Для каждой позиции мы хотели бы знать, какие ячейки указывают на нее.
1 2 4 5 4 1
5: 3
4: 2, 4 (leader)
3: None
2: 1
1: 0, 5
0: None
Теперь следуйте назад от лидера:
Who's looking at 4?
-> 2
[x, x, 1, x, 0, x]
Who's looking at 2?
-> 1
[x, 2, 1, x, 0, x]
Who's looking at 1?
-> 0 and 5
[3, 2, 1, x, 0, 3]
Who's looking at 0 or 5?
-> 3
[3, 2, 1, 4, 0, 3]
Псевдокод:
// For each position, we'd like to know
// which cells are pointing to it
A = input array
leader = None
map = {}
for i=0 to length(A)-1:
if i = A[i]:
leader = i
if i != leader:
if A[i] in map:
map[A[i]].append(i)
else:
map[A[i]] = [i]
//Now follow backwards from the leader
output = Array(length(A))
next = leader
output[leader] = 0
rank = 0
// Assumes the data provides
// for a straightforward solution.
// There may be edge cases to work
// out if that's not the case.
while next:
for i in map[next]:
next = None
if i in map:
next = i
rank = rank + 1
for j in map[next]:
output[j] = rank
break