4

単純な座標変換により、2 次元配列と 1 次元配列を同等に使用できることが知られています。そのような等価性は C++ 標準によって保証されているのでしょうか、それともデータを整理する最も便利な方法かもしれませんが、どこでも従う必要はありませんか? たとえば、次のコードはコンパイラに依存しませんか?

std::ofstream ofbStream;
ofbStream.open("File", std::ios::binary);
char Data[3][5];

for(int i=0; i<3; ++i)
for(int j=0; j<5; ++j)
{
    Data[i][j] = (char) 5*i+j;
}

ofbStream.write(&Data[0][0], 15);

ofbStream.close();

プログラムは、0、1、2、...、14 の数字をファイルに書き込むことが期待されています。

4

5 に答える 5

4

実際には、これで問題ありません。それを行わないコンパイラは、既存のコードに数え切れないほどの問題を抱えています。

ただし、非常に厳密に言えば、必要なポインター演算は未定義の動作です。

char Data[3][5];
char* p = &Data[0][0];
p + 7; // UB!

5.7/5 (強調地雷):

整数型の式をポインターに加算またはポインターから減算すると、結果はポインター オペランドの型になります。ポインター オペランドが配列オブジェクトの要素を指し、配列が十分に大きい場合、結果は元の要素からオフセットされた要素を指し、結果と元の配列要素の添え字の差が整数式に等しくなります。... ポインターオペランドと結果の両方が同じ配列オブジェクトの要素を指している場合、または配列オブジェクトの最後の要素の 1 つ後ろを指している場合、評価はオーバーフローを生成しません。それ以外の場合、動作は未定義です。

標準は、すべての配列要素がメモリ内で隣接し、特定の順序であること、および正しいアドレスを持つポインターを逆参照すること (取得方法に関係なく) がそのアドレスのオブジェクトを参照することを保証しますが、そうではありません。同じ配列の要素または最後を過ぎた要素を指していないため、p + 7予測可能なことを保証します。(代わりに、同じ配列の要素の要素を指します。)pp + 7

于 2013-05-24T17:26:49.467 に答える
4

彼の著書The C++ Programming Languageで、Bjarne Stroustrup は次のように述べています (C.7.2; p. 838 of the Special Edition、2000):

...ma次のように初期化できます。

void int_ma() {
    for(int i=0; i<3; i++)
        for(int j=0; j<5; j++) ma[i][j] = 10 * i + j; }

...

配列は、あたかも 5 の 3 つの配列であるかのようにアクセスするma単純な 15のs です。特に、メモリには行列である単一のオブジェクトはなく、要素のみが格納されます。次元 3 と 5 は、コンパイラ ソースにのみ存在します。intintma

(強調鉱山)。

つまり、表記法[][]...[]はコンパイラの構造です。必要に応じて構文糖衣。

娯楽目的で、次のコードを書きました。

#include<cstdlib>
#include<iostream>
#include<iterator>
#include<algorithm>

int main() {
  double ma[5][3]; double *beg = &ma[0][0]; // case 1
  //double ma[3][5]; double *beg = &ma[0][0]; // case 2
  //double ma[15]; double *beg = &ma[0]; // case 3

  double *end = beg + 15;

  // fill array with random numbers
  std::generate(beg, end, std::rand);

  // display array contents
  std::copy(beg, end, std::ostream_iterator<double>(std::cout, " "));
  std::cout<<std::endl;  
  return 0;
}

また、コンパイル コマンド (GCC 4.7.2) を使用して、3 つのケースで生成されたアセンブリを比較しました。

g++ test.cpp -O3 -S -oc1.s 

ケースはc1.sc2.s、および と呼ばれc3.sます。コマンドの出力shasum *.sは次のとおりです。

5360e2438aebea682d88277da69c88a3f4af10f3  c1.s
5360e2438aebea682d88277da69c88a3f4af10f3  c2.s
5360e2438aebea682d88277da69c88a3f4af10f3  c3.s

ここで、最も自然な構成は の 1 次元宣言ma、つまりのように思われることを述べておかなければなりませんdouble ma[N]。最初の位置は単純maに であり、最終的な位置は単純に であるからですma + N(これは、最初の配列の要素)。

この場合、C++ 標準ライブラリ ヘッダーによって提供されるアルゴリズムが<algorithm>はるかにぴったりと適合することがわかりました。

最後に、可能であればstd::arrayorの使用を検討することをお勧めします。std::vector

乾杯。

于 2013-05-24T16:58:05.280 に答える
1

C++ は、多次元配列を、メモリ全体に拡張される 1 次元配列として行優先順に格納します。

于 2013-05-24T16:58:17.877 に答える