web-dev-qa-db-ja.com

C ++スタック上でメモリを動的に割り当てる方法

ヒープの代わりに stack にメモリを割り当てる方法はありますか?これに関する良い本を見つけることができません。

31
Mark

alloca() (__alloca()または _malloca() と呼ばれることもあります)を使用しますが、 非常に注意してくださいabout —スコープ外に出たときではなく、関数を離れたときにメモリを解放するため、ループ内で使用するとすぐに爆発します。

たとえば、次のような関数がある場合

_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()は、ループを通るたびにadditionalnDataSizeバイトを割り当てます。 alloca()バイトは、関数から戻るまで解放されません。したがって、nDataSizeが1024でiterationsが8の場合、戻る前に8キロバイトを割り当てます。 nDataSize = 65536およびiterations = 32768がある場合、合計65536×32768 = 2,147,483,648バイトを割り当てます。ほとんど確実にスタックを破壊し、クラッシュを引き起こします。

anecdote:バッファの終わりを超えて書き込むと、特にバッファを別の関数に渡し、そのサブ関数がバッファの長さについての間違った考え。 以前はかなり面白いバグを修正しました GPUメモリに送信する前にTrueTypeフォントグリフをレンダリングするための一時ストレージを作成するためにalloca()を使用していました。フォントライブラリは、グリフのサイズを計算するときにスウェーデン語のÅ文字の発音区別符号を考慮していなかったため、レンダリング前にグリフを格納するためにnバイトを割り当て、実際にレンダリングn+ 128バイト。余分な128バイトがコールスタックに書き込まれ、リターンアドレスが上書きされ、非常に苦痛な非決定的なクラッシュが発生しました。

39
Crashworks

これはC++でタグ付けされているため、通常は必要なオブジェクトを正しいスコープで宣言するだけです。これらはスタックに割り当てられ、スコープの終了時に解放されることが保証されています。これは [〜#〜] raii [〜#〜] であり、Cに対するC++の重要な利点です。mallocsまたはnewsなし、特にallocas、必須。

5
Steve Townsend

ローカルchar[1024]または任意のバイト数(ポイントまで)を宣言し、スタック上のこのメモリブロックへのポインターのローカルアドレスを取得できます。正確に動的ではありませんが、必要に応じて、このメモリを独自のメモリマネージャでラップできます。

3
DuckMaestro

動的メモリ割り当てについて議論している記事

関数_allocaを使用して、スタックメモリに可変長スペースを動的に割り当てることができます。この関数は、プログラムスタックからメモリを割り当てます。割り当てられるバイト数を取得し、malloc呼び出しと同様に割り当てられたスペースにvoid *を返します。この割り当てられたメモリは、関数の終了時に自動的に解放されます。

したがって、明示的に解放する必要はありません。スタックオーバーフロー例外が発生する可能性があるため、ここで割り当てサイズについて留意する必要があります。このような呼び出しには、スタックオーバーフロー例外処理を使用できます。スタックオーバーフロー例外の場合、_resetstkoflw()を使用して元に戻すことができます。

したがって、_allocaを使用した新しいコードは次のようになります。

int NewFunctionA()
{
   char* pszLineBuffer = (char*) _alloca(1024*sizeof(char));
    …..
  // Program logic
     ….
  //no need to free szLineBuffer
  return 1;
}
2
Sammy

見る - _malloca

1
Dan

C++が(非静的な)const値の配列境界の使用を許可している場合は、より簡単になります。

今のところ、私が知っている最良の方法は再帰を経由することです。実行できるあらゆる種類の巧妙なトリックがありますが、私が知っている最も簡単な方法は、ルーチンに固定サイズの配列を宣言させ、その配列に値を入れて操作することです。完了すると、終了するためにより多くのスペースが必要な場合、自身を呼び出します。

1
T.E.D.

[〜#〜] 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
JDiMatteo