質問
これはから出てきました 私の以前の質問に対するこの答え。コンパイラーが処理することは保証されていますか? array[4][4]
と同じ array[16]
?
たとえば、以下のいずれかの呼び出しは、 api_func()
安全ですか?
void api_func(const double matrix[4][4]);
// ...
{
typedef double Matrix[4][4];
double* array1 = new double[16];
double array2[16];
// ...
api_func(reinterpret_cast<Matrix&>(array1));
api_func(reinterpret_cast<Matrix&>(array2));
}
解決
C++ 標準から、を参照すると、 sizeof
オペレーター:
配列に適用すると、結果は配列内の合計バイト数になります。これは、配列のサイズが
n
要素はn
要素のサイズの倍になります。
このことから言えることは、 double[4][4]
そして double[16]
同じ基礎となる表現を持つ必要があります。
つまり、与えられた
sizeof(double[4]) = 4*sizeof(double)
そして
sizeof(double[4][4]) = 4*sizeof(double[4])
それなら私たちは持っています
sizeof(double[4][4]) = 4*4*sizeof(double) = 16*sizeof(double) = sizeof(double[16])
標準に準拠したコンパイラーはこれらを同じように実装する必要があると思いますが、これはコンパイラーが誤って壊すものではないと思います。多次元配列を実装する標準的な方法は、期待どおりに機能します。標準を破ると追加の作業が必要になりますが、おそらくメリットはありません。
C++ 標準では、配列は連続して割り当てられた要素で構成されているとも規定されており、これにより、ポインターやパディングを使用して奇妙な操作が行われる可能性が排除されます。
他のヒント
を導入することによってパディングが導入されても問題はないと思います。 多次元 配列。
配列内の各要素は、アーキテクチャによって課されるパディング要件を満たさなければなりません。配列 [N][M] は常に [M*N] の 1 つと同じメモリ表現になります。
各配列要素は、コンパイラによってメモリ内に順番に配置される必要があります。2 つの宣言は、型は異なりますが、基礎となるメモリ構造は同じです。
@コンラッド・ルドルフ:
私自身、この 2 つ (行メジャー/列メジャー) を混同していますが、次のことは知っています。それは明確に定義されています。
たとえば、int x[3][5] はサイズ 3 の配列で、その要素はサイズ 5 の int 配列です。(§6.5.2.1) 配列、アドレス指定などに関する標準のすべてのルールを追加します。2 番目の添え字は連続する整数を参照するのに対し、最初の添え字は連続する 5-int オブジェクトを参照することがわかります。(つまり、3 のほうが大きい数です。間には5つのintがあります ×[1][0]と ×[2][0].)
Matrix[5][5] などの場合、各行の単語を揃えるためにパディングが追加されるのではないかと心配になりますが、それは単に私自身の迷信かもしれません。
さらに大きな疑問は次のとおりです。本当にそのようなキャストを行う必要がありますか?
あなたですが かもしれない 回避できても、完全に回避したほうが読みやすく保守しやすいでしょう。たとえば、実際の型として double[m*n] を一貫して使用し、この型をラップするクラスを操作して、使いやすくするために [] 演算子をオーバーロードすることができます。その場合、my_matrix[3][5] のようなコードが期待どおりに動作するように、単一行をカプセル化するための中間クラスも必要になる場合があります。