2次元配列は不連続か?
本家の方でちょっと議論になったのですが、英語には弱く知識も不十分ということもあって英語での議論では相手の主張がよく納得ができなかったのでこちらで質問させて頂きたいと思います。
簡単な例を挙げれば、short array[n][m];
という配列がある時、
short *p = &array[0][0];
for(int i = 0;i < n*m; ++i)
*p++ = 1;
は、
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
array[i][m] = 1;
と
同じ挙動をすることが保証されているか?
言い換えれば、2次の配列はその最小要素(つまりshort int
)が連続していることが保証されているか?
ということです。
標準では、「配列はその要素は連続しており隙間はない」とされていますが、
その方の主張によると、
「1次元配列は確かにそうであるが、2次元配列は必ずしもそうではない。」
例えばshort a[2][7];
のような副配列を考える時、
Cでは2次元配列は配列の配列であり、その意味で要素としてshort [7];
は隙間なく連続しているが、
副配列であるshort [7];
の最後には調整パディングが付与されているかもしれない。
そのような場合2次元配列全体としてみると連続していない。
(つまり先述の例のような場合の動作は保証されない)
確かに副配列の最後にパディングが存在しても標準の言と矛盾しないとは思いますが、
そもそもそのような(配列でパディングが付与される)ことがあるのかどうかも疑問です。
(構造体でメンバー間でパディングがある場合があることは承知しています、しかし配列の最後にパディングが付与されsizeof(short[7])
が8*sizeof(short)
になるとは思えません。)
例えば、short a[4];
もshort b[5];
もアラインメントの問題なくアクセスできます、
そのような手段がある(つまり最小構成要素であるshort
にアクセスできる)中でshort a[14]
(連続している)を解釈上short a[2][7];
(不連続である?)と内部の表現が違うというのは納得できません。
そのようなパディングがそもそも必要無いと思うし、
もしそのようなパディングが存在するような内部イメージを持つならアドレス演算を複雑にするだけだと思います。(Cの理念に反する?)
また、対象としている配列が
sizeof(array) == n*m*sizeof(array[0][0])
が真である場合にはそのようなパディングが存在しない、
なので、この場合連続であるといってもよく、先述の例の動作は保証される。
と思ったのですが、
「それでも動作は保証されない。境界制約のルールに違反する。」
と言われました。
私の理解としては、short *p = &array[0][0];
のようなことは最小構成要素が同じshort
であり、連続している場合(つまりメモリのサイズが同じでパディングを有していないと考えられる場合)問題無いと思えます。(逆に最小構成要素が異なる場合、例えばchar *
をint *
に変換してint
でアクセスすることは問題がある。)
私の理解が間違っているとしたら何が間違っているのでしょうか?
このようなこと(連続か不連続か)をハッキリさせるような文言が標準にありますか?
長くて要点がはっきりしない部分もあると思うのでまとめを追加します。
Q:多次元配列は不連続か?(不連続の意味合いの説明はもういいと思うので略)
A:
標準に記載がない以上、連続かも知れないし、不連続かもしれない。
不連続かも知れないので不連続だとして扱う必要がある。
(つまりサンプルコードの動作は保証されないものとしなければならない)
Q:では、連続すると判断ができた場合のサンプルコードの動作は保証されるか?
A:(私の理解では保証される)
Q:自分のシステム+コンパイラで連続する(あるいは不連続)と判断するためにどのようなコードを書けばよいか?
A:(私の理解ではsizeof(array) == n*m*sizeof(array[0][0])
のようなもの)