C ++ Как динамически распределять память по стеку? - PullRequest
30 голосов
/ 13 июня 2011

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

Ответы [ 7 ]

38 голосов
/ 13 июня 2011

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

Например, если у вас есть такая функция, как

int foo( int nDataSize, int iterations ) 
{
   for ( int i = 0; i < iterations ; ++i )
   {
      char *bytes = alloca( nDataSize );
      // the memory above IS NOT FREED when we pass the brace below!
   } 
   return 0;
}  // alloca() memory only gets freed here

Затем alloca () будет выделять дополнительные nDataSize байтов каждый раз через цикл .Ни один из байтов alloca () не освобождается, пока вы не вернетесь из функции.Итак, если у вас nDataSize 1024 и iterations 8, вы выделите 8 килобайт перед возвратом.Если у вас есть nDataSize = 65536 и iterations = 32768, вы выделите в общей сложности 65536 × 32768 = 2 147 483 648 байт, почти наверняка взорвав свой стек и вызвав сбой.

anecdote: Вы можете легко попасть в неприятности, если будете писать после конца буфера, особенно если вы передаете буфер в другую функцию, и эта подфункция имеет неверное представление о длине буфера. Однажды я исправил довольно забавную ошибку , когда мы использовали alloca() для создания временного хранилища для рендеринга глифа шрифта TrueType перед отправкой его в память графического процессора.Наша библиотека шрифтов не учитывала диакритический знак в шведском символе Å при расчете размеров глифов, поэтому она предложила нам выделить n байтов для хранения глифа перед рендерингом, а затем фактически отрисовать n + 128 байт.Дополнительные 128 байтов записываются в стек вызовов, перезаписывая адрес возврата и вызывая действительно болезненный недетерминированный сбой!

5 голосов
/ 13 июня 2011

Поскольку это тег C ++, обычно вы просто объявляете нужные объекты в правильной области видимости. Они размещаются в стеке и гарантированно освобождаются при выходе из области. Это RAII и критическое преимущество C ++ по сравнению с C. Не требуется malloc s или new s и, особенно, alloca s.

3 голосов
/ 13 июня 2011

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

2 голосов
/ 21 октября 2011

Статья, посвященная динамическому распределению памяти

Мы можем динамически распределять пространство переменной длины в памяти стека с помощью функции _alloca.Эта функция выделяет память из стека программ.Он просто занимает количество байтов для выделения и возвращает void * выделенному пространству так же, как вызов malloc.Эта выделенная память будет автоматически освобождена при выходе из функции.

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

Таким образом, наш новый код с _alloca будет:

int NewFunctionA()
{
   char* pszLineBuffer = (char*) _alloca(1024*sizeof(char));
    …..
  // Program logic
     ….
  //no need to free szLineBuffer
  return 1;
}
1 голос
/ 07 ноября 2014

Вы можете использовать библиотеку BDE C ++, например,

const int BUFFER_SIZE = 1024;
char      buffer[BUFFER_SIZE];

bdlma::BufferedSequentialAllocator allocator(buffer, BUFFER_SIZE);
bsl::vector<int>                   dataVector(&allocator);

dataVector.resize(50);

BDE предоставляет комплексные опции распределителя наряду с коллекциями, такими как bsl :: vector, которые могут использовать полиморфные распределители без изменения типа контейнера.

Вы также можете рассмотреть:

1 голос
/ 13 июня 2011

Когда / если C ++ разрешает использовать (нестатические) const значения для границ массива, это будет проще.

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

1 голос
/ 13 июня 2011

См. _malloca.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...