27

本家の方でちょっと議論になったのですが、英語には弱く知識も不十分ということもあって英語での議論では相手の主張がよく納得ができなかったのでこちらで質問させて頂きたいと思います。

簡単な例を挙げれば、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])のようなもの)

BLUEPIXY
  • 6,907
  • 1
  • 21
  • 34
  • 1
    とりあえず、本家の記事へのリンクを提示してもらえるとありがたいですが。 – kmugitani Jan 22 '15 at 00:38
  • それと、「標準では、『配列はその要素は連続しており隙間はない』とされています」の部分のソースですね。多次元配列は結局は一次元配列の先頭アドレスの集合だと思うので連続である必要はない気がしますが・・・ – kotatsu Jan 22 '15 at 00:41
  • @kmugitani この回答のコメントでやりとりしています。char[7] にはそもそも境界問題がそれ自体には存在しないと思うのでshortに変えています。 – BLUEPIXY Jan 22 '15 at 00:41
  • 1
    @kotatsu n1256.pdf6.2.5 Types 20 の部分ですね。 これ自体はC99のドラフトですが、ネットからリンクできるソースとして使えると思います。 – BLUEPIXY Jan 22 '15 at 00:44
  • 2
    逆に、連続でないような実装を禁止する文言を見つけることができないのですが、どの部分からそのように判断されたのでしょうか? 6.2.5の20からは、Tの配列が連続したTの列であること、Tの配列の配列が連続したTの配列の列であることを示していますが、Tの配列の配列が連続したTの列であることは示していなように思います。 – snak Jan 22 '15 at 00:55
  • @snak そうですね。私の理解もそうであっても矛盾はないというように理解しています。(この質問でも「矛盾しない」と書きました)ただ、そんな必要は無いと思えます。そうするとやはり不連続な場合がやはりあるとするべきなんでしょうね、その場合その判別はsizeof(array) == n*m*sizeof(array[0][0]) ではできないのでしょうか?そして連続であるとなんらかの方法で判断できたならば問題無いのでしょうか? – BLUEPIXY Jan 22 '15 at 01:02
  • Tの配列の配列が連続したTの配列の列であるためには、例えば、int (*p)[N]のような配列へのポインタがあった時に(Nは整数)、&p[1] == (int (*)[N])((char *)p + sizeof(int[N]))が成り立たないといけないということだとします。

    ここで、仮に配列の後ろにパディングが入るような実装であるにもかかわらず、sizeof(int[N])sizeof(int)*Nであるとすると、上記の条件が成り立たなくなります。ということは、sizeof(array) == n*m*sizeof(array[0][0])であれば連続であると考えて良いと思います。

    また、6.5.3.4の6に、sizeof array / sizeof array[0]で配列の要素数が取れるという例が挙げられていますが、これが仕様上保証されるならば、当初の、二次元配列は仕様上に連続かどうかという問題は、連続であることになります。例なので仕様外かもしれませんが。

    ただし、最初に提示した、連続である場合の仮定が、仕様上の定義であるのかいまいちわかりません。

    – snak Jan 22 '15 at 01:52
  • 4
    今回のケースはテストコードは意味が無いです。というのも、規格として「多次元配列は連続領域でなければならない」と記載されていないのであれば、多次元配列の実装方法はコンパイラ任せだからです。ここの実装が変われば、多次元配列におけるsizeofの実装も変わります。コンパイラが変われば結果が変わるということもあり得ます。 – kotatsu Jan 22 '15 at 02:09
  • @snak 連続であるという判断がそれでできるというのは私の望むものですが、やはり論理的に正しいですよね。 そして、「連続であれば挙動は保証される」と言ってもよいでしょうか? (後半部分について)そうですよね。正しい要素数がとれないということは問題だと思っています。(charの場合顕著しかし普通charでは境界問題がないからする必要も無い、サイズ1以上で要素のサイズ未満の場合は切り捨てられて問題にならない) – BLUEPIXY Jan 22 '15 at 02:50
  • @kotatsu つまり連続かどうかは判定する方法がなく、規定する記載がない以上多次元配列は不連続であるとして扱うべきである。ということですね。 – BLUEPIXY Jan 22 '15 at 02:53
  • 「規格上の保証がない」という主張には、2つの段階があるように思えます(特に本家SOのlanguage-laywerタグでは。)1)探している仕様に対する直接的な記載がない、2)直接的な記載はないが関連仕様から演繹すると明らかにそれ以外の実装は存在しえない。私は今回の件は、「仕様上に直接記載はされない、ただし事実上は保証される」と考えています。またchar型の件は、境界問題というよりstrict aliasingに関する言及な気がします。 – yohjp Jan 22 '15 at 03:05
  • 1
    @kotatsu 直接記述されていなくても、仕様内の記述から演繹的に導き出せるかどうか、というのが今回の質問(の前半部分)だと思うので、直接書いていないから実装依存であるというのは乱暴すぎるのではないかと思います。現実には、仕様に則っていないコンパイラも沢山あるので、拘っても仕方のないことなのかもしれませんが。 – snak Jan 22 '15 at 03:05
  • @yohjp そうですね。実際におかしな挙動をするプログラムを吐き出す標準コンパイラがあればこれがそうか!って思いますけど。実際そのようなパディングを配列に付与するコンパイラの吐き出すコードはかなり挙動がおかしいと思えるだろうと思います。(通常当たり前に思って書いているコードが思うように動かないようなことが色々な部分ででてくるだろうと思う) – BLUEPIXY Jan 22 '15 at 03:15
  • @snak うーん・・・sizeofの実装次第では提示されたコードによって連続領域を判断できないような・・・パディングを追加するしないがコンパイラの自由だとして、sizeofは連続領域であるように結果を返すことだってあると思うのですが(パディングの追加はコンパイラの都合でしかないため)・・・少し穿ち過ぎですかね。 – kotatsu Jan 22 '15 at 03:20
  • @BLUEPIXY 不連続として扱うべきというか、規格から考えれば有り得るというだけで、私も多次元配列にパディングが入ったり不連続だったりするようなコンパイラは使いたくないというか・・・(笑)基本的には連続であると考えても差し支えないと思います。ただ、その確認コードを盲信はできませんよ、というだけです。メモリが少ないことを想定しているコンパイラはどうしても多次元配列は各配列毎に別に持たないといけないこともあるでしょう(それか自分で管理する?)。 – kotatsu Jan 22 '15 at 03:48
  • @kotatsu 連続していないにもかかわらず、sizeofで連続しているかのように返す(パディングを含めないサイズを返す)のは、仕様に違反しているのではないか、というのが先のコメントの意味するところです。具体的な例を挙げると、array[3][5]があった時に、sizeof array[0]が5であれば、この二次元配列は連続していると、仕様的には言えるのではないかということです。現実的には、組み込み用のコンパイラなどでは、これを満たさないものがあるであろうことは想像できますが。 – snak Jan 22 '15 at 03:59
  • @snak 話が2つありますね。1. sizeof()がパディングを考慮するのか。これは仕様上する(メモリの占有サイズを返している?)ということですね?このあたりは理解不足でした。2. 2次元配列が連続領域か。short array[2][7]sizeof(array)したときに2*7*sizeof(short)と等しいからといって連続領域という判断はできない思うのですが・・・「本当に」厳密にやるならアドレスの連続性を確認する必要がある気がします。(オーバースペックですね・・・) – kotatsu Jan 22 '15 at 04:27
  • この流れも非常に有益なのですが、もとの回答は 'this is the case only for dynamically allocated arrays,' とあるので、彼が想定しているケースは short **arrays = (short**)malloc(sizeof(short*) * 2); arrays[0] = (short*)malloc(7); arrays[1] = (short*)malloc(7); じゃないかな、と、私は行間じゃないかもしれないなにかを読みました。(これならズレる気がします。) – ohga Jan 22 '15 at 09:32
  • @ohga もとの回答のその部分は変更された部分です。私と議論相手の話は"dynamically allocated arrays"ではないと思われる配列の話でその編集前の回答で始まってます。なので、”動的に確保された配列”等々については、私の質問では考慮しなくていいです。 – BLUEPIXY Jan 22 '15 at 09:42
  • 1
    つまり私の質問はジャグ配列ではなくいわゆるリニアな配列についての話で回答主には直接関係有りません。(回答のコメント欄でこんな議論始められたら迷惑至極ではありますねw) – BLUEPIXY Jan 22 '15 at 09:50

5 Answers5

11

私の解釈では以下の通りです。

  1. 2次元配列要素の連続性保証は直接明言されないが、演繹的には連続したメモリ配置であるといえる。
  2. ただし、2次元配列上での1次元配列的な要素走査(*p++)は、その動作保証がなされない。
  3. 以上より、質問文中のサンプルプログラムは同じ挙動を示す。(そうならないC言語処理系は合理的に存在し得ないという態度)

以降の解釈はWG14/N1256 ISO/IEC 9899:TC3(C99言語仕様)に基づきます。


  1. 2次元配列要素の連続性保証は直接明言されないが、演繹的には連続したメモリ配置であるといえる。

6.2.5/p20により、1次元配列(short [7])ではその要素(short)の連続性が明らかに保証されます。2次元配列(short [2][7])の要素は1次元配列型(short [7])であり、この1次元配列型の連続性が明らかに保証されます。2次元配列(short [2][7])の全要素の連続性が保証されるか否かは、その配列要素型short [7]同士の"間"にパディングバイトが存在しえるかという問題に帰着できます。

20 Any number of derived types can be constructed from the object, function, and incomplete types, as follows:

  • An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. [...]
  • [...]

6.5.3.4/p2-3にはsizeof演算子は「オペランド式の型が占めるバイトサイズを返す」「オペランドが配列型の場合は、配列が占める総バイトサイズを返す」とあります。

2 The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

3 [...] When applied to an operand that has array type, the result is the total number of bytes in the array. When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding.

short array[2][7];では、sizeof(array[0][0])short型サイズ(例えば2)、sizeof(array[0])sizeof(short[7])つまりshort型×2sizeof(array)sizeof(short[2][7])つまりsizeof(short[7])x2short型×2×7となるべきです。先ほど懸念したパディングがどこかに存在すると仮定した場合、この制約を満たすことができなくなるため、パディングバイトはその存在が許されないと考えます。(下図参照)

// 要素short型が*連続配置*された配列short[7]
+---+---+-//-+---+
| s | s | .. | s |  sizeof(short)*7 == sizeof(short[7])
+---+---+-//-+---+

// 要素(short[7])型が*連続配置*された配列short[2][7]
+------+------+
| s[7] | s[7] |  sizeof(short[7])*2 == sizeof(short[2][7])
+------+------+

これらより、配列要素数の計算式が期待通り動作すると保証されています。(6.5.3.4/p6は例示にすぎず、原理主義的立場では仕様(normative)ではありませんが、前掲解釈を増補するものとみなせます。)

6 EXAMPLE 2 Another use of the sizeof operator is to compute the number of elements in an array:
sizeof array / sizeof array[0]


補足:@snakさんに指摘頂いた、配列実装のメモリレイアウトとして「末尾パディングはあり得ないのか?」という点について、英語版StackOverflowに質問"Can an array have trailing padding?"がありました。付いていた回答の主張は、私の解釈と同じで「そうする理由が存在しない」という点です。また、6.5.9/p6 ==演算子セマンティクスを根拠として提示されていました。

6 Two pointers compare equal if and only if both are null pointers, [...] or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space. 94)

脚注94) Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior.


2点目については、C99仕様の6.5/p7、いわゆるStrict Aliasing Ruleにより保証がないと当初考えていました。ただ、改めて確認すると問題無いように思えたので、こちらの主張は取り下げます。

yohjp
  • 10,194
  • 2
  • 22
  • 91
  • 上記の"the total number of bytes in the array"が、パディングを含めたバイト数であったとしても、この記述には違反していないように読めるのですが、いかがでしょうか? – snak Jan 22 '15 at 04:52
  • @snak 該当文面だけの解釈ではその通りですし、structではパディングを含むバイトサイズを返します。私の解釈のポイントは「多次元配列に対するsizeof演算子の仕様から演繹的に解釈すると、配列型では要素以外のパディングの存在が許容されない」という点です。また、6.5.3.4/p6例示は恒に成り立つべきであるという仮定もあります。(これは主張の増補材料としてですが) – yohjp Jan 22 '15 at 05:04
  • 「多次元配列に対するsizeof演算子の仕様から演繹的に解釈すると」の部分がよくわからないのですが、もう少し説明して頂けませんか。6.5.3.4 p6の例に関しては、質問へのコメントにも書いたように、同意します。 – snak Jan 22 '15 at 05:08
  • @snak 図解を入れてみました。各配列で連続配置が保証される部分と、sizeof演算子が返すべき値の関係性から導いています。 – yohjp Jan 22 '15 at 05:26
  • 1
    ありがとうございます。まだ釈然としないので、ちょっと質問を変えます。short[7]を、shortのフィールドを7個持ち最後に2バイトのパディングがある(フィールド間にはパディングがない)構造体と同じメモリレイアウトで実装することが禁止されている、と考える根拠は何でしょうか?(sizeof short[7]は16を返すとします)6.5.3.4 p6の例に反するということ以外の根拠を自分では見つけられずにいます。 – snak Jan 22 '15 at 05:35
  • 1
    @snal 正直なところ、"パディング有レイアウトが禁止と考える根拠"は、1)6.5.3.4/p2文面で配列へのパディング言及がない一方で構造体にはその明記があること、2)多次元配列(T[2][7])の要素(T)を連続配置してもアライメント上の問題は起きえないこと、3)以上より処理系で冗長なオブジェクト表現(with padding)を選択する合理的理由が思いつかないこと、という程度です。 – yohjp Jan 22 '15 at 06:02
  • 回答に補足していただいた点も確認しました。==の意味がここに影響を与えるのかというと微妙な気がします(メモリ上で偶然並んだ場合についてしか言及していないので)が、仰りたいことは理解できましたし、納得できます。ありがとうございました。 – snak Jan 22 '15 at 07:24
  • 回答ありがとうございました。私的には特に問題があるようには思えませんのでこの回答を支持したいと思います(質問している私には正誤判定などできません)が、反論回答がまだあるかもしれないのでしばらく待ちたいと思います。 "Strict Aliasing Rule" は、T[2][7]の時[0][8]にアクセスすることは違反という理解でいいですか? 彼/彼女はこのことについて言っていたのですね。 パディングの話からだったのでアラインメントの話だと勘違いしていました。これは通常日本語ではなんと呼称されていますか? よろしければ教えて下さい。 配列の中身がリニアに連続である場合(しかも、きちきちに同じメモリを有するなら)、T[2][7]とT[14]は同じメモリイメージを有しており相互に解釈し直せる、つまりT *を使ってリニアにアクセスできる。という理解(つまりサンプルプログラムは同じ挙動を示す)でよろしいでしょうか? – BLUEPIXY Jan 22 '15 at 09:11
  • 1
    Strict Aliasing Ruleに関する懸念は、short a[2][2] = { 0, 1, 2, 3 }; short *p = &a[0][0] + 3; *p = 10; short n = a[1][1];のようなコードを書いた時に、n10ではなくて3になるような最適化をコンパイラがすることが許されるか、というような話ですね。しかし、この場合には、そのような最適化を許すようなルールはない、という話だと思います。 – snak Jan 22 '15 at 09:46
  • @BLUEPIXY 100%の確信ではないのですが、サンプルプログラムは同じ挙動を示すと解釈しています(そうならないC言語処理系は合理的に存在しないという態度)。あと、「T[2][7]の時[0][8]にアクセスすることは違反」ですが、これは単なる配列範囲外アクセスであり、Strict Aliasing Rulesとは関係ありません。"Strict Aliasing Rules"の日本語対訳は、特にコレといったものが無い気がします(そのまま英語表記が多い)。JPCERT EXP39-Cも参考になるかと思います。 – yohjp Jan 22 '15 at 09:58
  • @snak @BLUEPIXY Strict Aliasing Ruleに関する当初の懸念は、まさにご指摘の通りでした。式*pまたは式a[1][1]に関わらず、最終的なオブジェクトアクセスはshort型左辺値に対して行われるため、snakさんが指摘されるように最適化されることは無いと解釈しています。 – yohjp Jan 22 '15 at 10:04
  • @yohjp またまた勘違いでしたか。 なるほどリンク先は具体的なコードもあってわかりやすいですね。“最終的なオブジェクトアクセスはshort型左辺値に対‌​して行われる” なるほどです。ということは、彼/彼女の"Strict Aliasing Rule"に関しての言及は誤りですね。 – BLUEPIXY Jan 22 '15 at 10:11
4

タグがCなのでオフトピ気味になりますが、C++だと、配列のサイズが要素のサイズ×要素数とあるので、配列の後ろにパディングが付かないことになりますね。

5.3.3 Sizeof p6 より引用:

When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of n elements is n times the size of an element.

ISO/IEC 14882:2003, 2011, 2014 で、上記の存在を確認しました。

yoh2
  • 2,348
  • 2
  • 13
  • 29
1

あえて仕様を当たらず。

一般に配列a、型typeについて、

  1. &a[N]a + Nと常に同義である。
  2. a + N の結果は常に&a[0] + sizeof(*a) * N(ポインタ型を考慮しない単純加算)と一致する。
  3. sizeof(type[M])は常にsizeof(type) * Mと一致する。

ことが保証されていれば、連続していると言えると思います。
(そしてそれぞれ保証されているはず)

つまり

short array[2][7];
があったとき

  1. &array[1] = array + 1
  2. array + 1 = &array[0] + sizeof(*array) * 1 (ポインタ型を考慮しない単純加算)
  3. sizeof(*array) = sizeof(short[7]) = sizeof(short) * 7

ゆえに
&array[1] = &array[0] + sizeof(short) * 7 (ポインタ型を考慮しない単純加算)

この式は領域が連続していることを示していると思うわけです。

すなわち、メモリ内配置は

array[0][0]
array[0][1]
array[0][2]
array[0][3]
array[0][4]
array[0][5]
array[0][6]
array[1][0]
array[1][1]
array[1][2]
array[1][3]
array[1][4]
array[1][5]
array[1][6]

と連続しており、array[0][6]とarray[1][0]の間にパディングは存在し得ないということです。

Ripple
  • 1,307
  • 1
  • 8
  • 18
  • 論点としては「3.sizeof(type[M])は常にsizeof(type) * Mと一致」が仕様上保証されるか?という点でしょうね。仕様文面からこのことが直接読み取れれば、仰る通りのシンプルな証明で済むのですけど… – yohjp Jan 23 '15 at 04:56
  • @yohjp そうですね。質問に対する@kotatsuさんのコメントでは1.~3.が保証されてても不連続の可能性はある、と主張されているように見えたので、それに対する反論としてこの回答を書き始めた気がします。まあ、仕様を追求しなくても1.~3.は周知の事実だよねという、なんというか、厳密さよりもわかりやすさを優先した感覚的な回答もありかなぁとも思ったり思わなかったり。(単に私が仕様の解釈論争はあんまり好きじゃないだけという噂も) – Ripple Jan 23 '15 at 15:13
1

仮に多次元配列を1次元配列として扱えない処理系が存在するなら、それらの処理系ではcallocやmemset等を独自に拡張する必要があります。
規格がそういった処理系も考慮しているなら、独自拡張しなくても済む仕様になってるはず。

hoge
  • 95
  • 1
0

追記:yohjpさんとsnakさんの回答・コメントより、配列の配列も配列なんだから連続だ、ということが腑に落ちましたので下記回答は撤回します。

コメント等の情報も加味して回答します。
私の回答としては「不連続かもしれない」です。

前提の配列は以下。

short array[2][7];

※結果は妄想であって、各種コンパイラが用意できるはずもなく、検証はしていないです。

1. パディング入れるよ、多次元配列も連続領域だよコンパイラ

sizeof(array) == 32
2*7*sizeof(short) == 28

2. パディング入れるよ、多次元配列は連続領域じゃないよコンパイラ

sizeof(array) == 32
2*7*sizeof(short) == 28

3. パディング入れないよ、多次元配列も連続領域だよコンパイラ

sizeof(array) == 28
2*7*sizeof(short) == 28

4. パディング入れないよ、多次元配列は連続領域じゃないよコンパイラ

sizeof(array) == 28
2*7*sizeof(short) == 28

パディングなしの場合、sizeof(short[7])の評価は常に14で、それはarray[0][0]のアドレスが0x1000array[1][0]のアドレスが0x2000となっていても変わらないため、連続領域かそうでないかは区別が付かないと思いますがいかがでしょうか。

sizeofとパディングについて私が何か根本的に間違ってますか・・・?

kotatsu
  • 2,142
  • 4
  • 17
  • 31
  • @yohjpさんの解答にあるように、2次元配列を1次元配列の1次元配列と考えると、2次元配列中の1次元配列同士は連続して配置される必要があります。その前提ですと、4は仕様違反です。1に関しては、それが許されるのかどうか疑問です(例えば、array[0][6]を指すポインタをインクリメントすると、パディング部分を飛ばしてarray[1][0]を指すようになるということですよね?)これらを前提として、2と3を区別するために、sizeof(array)2*7*sizeof(array[0][0])を比較すればいいのではないか、というのが、質問の3番目であると理解しています。 – snak Jan 22 '15 at 05:16
  • あーなるほど。配列の配列も配列なので連続しているべき、ということですね。2.と4.は本来ないはずだ、と。 – kotatsu Jan 22 '15 at 05:23