2017-09-13 11 views
4

C++で2つの2D配列、スタックから割り当てられた配列、ヒープから割り当てられた配列の動作を調べています。ヒープとスタックに割り当てられた配列の動作が異なるのはなぜですか?

同じ形状の2次元配列を作成し、それらの配列にデータを取り込みます。次に、2つの異なるメソッドで配列を読み込もうとします。最初のものは単純な配列インデックスフォーマット "Arr [ROW] [COLUMN]"です。次に、私は、ポインタ逆参照を使用して配列を読み取り、ヒープ割り当て配列の2つの異なる結果が得られますが、スタック割り当て配列の結果は同じです。なぜ結果が違うのか理解しようとしています。私は誰かが提供できる明確化を感謝します。前もって感謝します。

#include <iostream> 

using namespace std; 

int main(){ 

    int rows = 6; 
    int columns = 3; 

    // allocate from the stack. 
    double q[rows][columns]; 

    // allocate from the heap. 
    double ** a; 
    a = new double*[rows]; 

    for(int i = 0; i < rows; ++i){ 
     a[i] = new double[columns]; 
    } 

    // populate the arrays. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      a[i][j] = columns*i+j; 
      q[i][j] = columns*i+j; 
     } 
    } 

    cout << "*****************" << endl; 
    cout << "Array indexing method." << endl; 
    cout << "*****************" << endl; 

    // print the heap allocated array using array indexing. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << a[i][j] << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 

    // print the stack allocated array using array indexing. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << q[i][j] << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 
    cout << "Pointer dereferencing method." << endl; 
    cout << "*****************" << endl; 

    // print the heap allocated array. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << *(&a[0][0] + columns*i + j) << '\t'; 
     } 
     cout << endl; 
    } 

    cout << "*****************" << endl; 

    // print the stack allocated array. 
    for(int i = 0; i < rows; ++i){ 
     for(int j = 0; j < columns; ++j){ 
      cout << *(&q[0][0] + columns*i + j) << '\t'; 
     } 
     cout << endl; 
    } 
    cout << "*****************" << endl; 

    // release the memory allocated to the heap. 
    for(int i = 0; i < rows; ++i){ 
     delete[] a[i]; 
    } 

    delete a; 

    return 0; 
} 

私が得た結果は次のとおり:

私が実行しているコードは、以下である、ヒープに割り当てられた配列のISN

***************** 
Array indexing method. 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 
Pointer dereferencing method. 
***************** 
0  1  2 
0  3  4 
5  0  6 
7  8  0 
9  10  11 
0  12  13 
***************** 
0  1  2 
3  4  5 
6  7  8 
9  10  11 
12  13  14 
15  16  17 
***************** 

そして、私は、出力の第3のブロックにそれを見ることができます正しく読み込まれていませんが、スタック割り当て配列はです。

もう一度おねがいします。

+3

'ダブルQ [行] [列]; 'VLAsは標準のC++ではありません。 – user0042

+0

これは問題ではありませんが、実際には 'std :: endl'が行う余分なものが必要ですか? '' \ n ''は行を終わらせます。 –

+0

いいえ、私はそうではありません、あなたはこの例のために正しいです。\ nは大丈夫でしょう。 –

答えて

11

&q[0][0]は、rows x columnsを含むブロック内の最初のダブルへのポインタを与えます。 &a[0][0]は、ブロック内の最初のダブルをcolumns倍にします(a[0] = new double[columns];を使用して割り当てましたか?)。したがって、columns*i + jにアクセスすると境界外になり、未定義の動作がトリガーされます。

+3

ああ、私の問題は、ヒープからの割り当てが連続していると仮定していることですが、実際にはそうではありませんか?おそらく、それぞれの列の列は他の列にかなり近いです。なぜなら、私の出力では、適切な値がいくつか得られているからです。 –

+2

@ JoshM。ヒープの割り当てはまったく連続していません。 'rows'個のポインタを含むブロックをdoubleに割り当て、' rows'個のブロックをそれぞれ 'double'個ずつ割り当てます。ヒープはまだ詰まっていないので、この特定のケースでは、それらは次々と割り当てられます(間にいくつかのパディングがあります)。しかし、アロケータの振る舞いについて、この種の仮定に頼るべきではありません。 – VTT

+0

これはあなたの答えに追加することができます: "ポインタ逆参照法"は、最初のサブ配列の境界を越えてインデックスを付けるので、スタック割り当て配列でもUBを呼び出します。メモリは連続していますが、これはまだ違法です。 –

3

アレイは通常、この場合はスタック上の単一の割り当てです。配列値q[1][0]は、q[0][columns-1]の直後に配置されています。ポインタの実装では、ヒープから各行の独立したメモリブロックを割り当てています。最初の行の最後を超えて参照すると、の定義されていない動作テリトリーになります。

2

スタックベースのアレイは完全に連続したメモリです。ヒープベースのヒープは、行ごとに1つの割り当てと、そのすべての割り当てを保持する割り当てです。割り当てごとのメモリーは、ヒープ全体に任意の順序で散在させることができ、それらの間には任意のスペースがあります。最初の行の割り当ての基底にオフセットを追加することは、その終わりを過ぎた場合に有効であると考えることはできません。以前の割り当ての終わりから十分離れて歩いて別の割り当てに到達しようとするバグです。

あなたがしようとしている何をしたい場合は、単一のアレイにフラット化するためにあなたの新しい[]式を変更:それは2D配列であるかのように

double * q = new [rows * columns]; 

その後、あなたはそれへのインデックスをすることができます。

はまた、あなたのオリジナルのポストに、あなたはまた、配列であり、一つとして削除する必要がある上、削除コールの間違った種類、使用(それの後に[]とする。)

関連する問題