アプリケーションにとってパフォーマンスが重要な場合、スタックとヒープのどちらで配列を宣言するかを考慮する必要がありますか? この質問が頭に浮かんだ理由を概説させてください。
C/C++ の配列はオブジェクトではなく、ポインターに分解されるため、コンパイラーは指定されたインデックスを使用してポインター演算を実行し、要素にアクセスします。私の理解では、この手順は、最初の次元を通過するときに、静的に宣言された配列と動的に宣言された配列とは異なります。
次のようにスタックで配列を宣言するとします。
int array[2][3] = { 0, 1, 2, 3, 4, 5 }
//In memory { row1 } { row2 }
この配列はメモリの連続したブロックに格納されるため、メモリに行メジャー形式で格納されます。つまり、配列内の要素にアクセスしようとすると、コンパイラは正しい位置を確認するために加算と乗算を実行する必要があります。
したがって、次のことを行う場合
int x = array[1][2]; // x = 5
コンパイラは、次の式を使用します。
i = 行インデックス j = 列インデックス n = 1 行のサイズ (ここでは n = 2)
配列 = 最初の要素へのポインタ
*(array + (i*n) + j)
*(array + (1*2) + 2)
つまり、この配列をループして各要素にアクセスすると、インデックスによるアクセスごとに追加の乗算ステップが実行されます。
現在、ヒープで宣言されている配列では、パラダイムが異なり、多段階のソリューションが必要です。注: ここで C++ の new 演算子を使用することもできますが、データの表現方法に違いはないと思います。
int ** array;
int rowSize = 2;
// Create a 2 by 3 2d array on the heap
array = malloc(2 * sizeof(int*));
for (int i = 0; i < 2; i++) {
array[i] = malloc(3 * sizeof(int));
}
// Populating the array
int number = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0l j < 3; j++) {
array[i][j] = number++;
}
}
配列は動的であるため、その表現は 1 次元配列の 1 次元配列です。アスキー絵を描いてみます...
int * int int int
int ** array-> [0] 0 1 2
[1] 3 4 5
これは、乗算がもはや含まれていないことを意味しますよね? 私が次のことをした場合
int x = array[1][1];
これにより、array[1] に対して間接/ポインター演算が実行されて 2 番目の行へのポインターにアクセスされ、これをもう一度実行して 2 番目の要素にアクセスされます。私はこれを言うのが正しいですか?
いくつかのコンテキストがあるので、質問に戻ります。フレームのレンダリングに約 0.016 秒かかるゲームなど、鮮明なパフォーマンスを必要とするアプリケーションのコードを書いている場合、スタックとヒープで配列を使用することについてよく考えるべきでしょうか? これで、malloc または new 演算子を使用すると 1 回限りのコストがかかることがわかりましたが、(Big O 分析と同様に) データ セットが大きくなる特定の時点で、行優先を回避するために動的配列を反復処理する方がよいでしょう。索引付け?