どちらa[0][19]
もa[1][-1]
、C 標準で定義された動作もありません。
C 2018 6.5.2/1 2 は、配列の添え字がポインター演算に関して定義されていることを示しています。
角括弧内の式が後に続く後置式[]
は、配列オブジェクトの要素の添え字指定です。添字演算子の定義は[]
…E1[E2]
と同じ(*((E1)+(E2)))
です</p>
したがってa[0][19]
、*(a[0] + 19)
(一部の括弧は不要であるため省略されています) と同じであり、a[1][-1]
と同じです*(a[1] + -1)
。
、a[0] + 19
、およびa[1] + -1
は配列です。これらの式では、C 2018 6.3.2.1 3 に従って、最初の要素へのポインターに自動的に変換されます。したがって、これらの式はand と同等です。ここで、 とはそれぞれ最初の要素のアドレスです。a[0]
a[1]
p + 19
q + -1
p
q
&a[0][0]
a[1][0]
C 2018 6.5.6 8 は、ポインター演算を定義します。
ポインターオペランドが配列オブジェクトの要素を指し、配列が十分に大きい場合、結果は元の要素からオフセットされた要素を指し、結果と元の配列要素の添字の差が整数式と等しくなります。つまり、式が配列オブジェクトのiP
番目の要素を指している場合、式(同等に、) と(値nを持つ場所) は、それぞれi + n番目とi − n番目の要素を指しています。存在する場合、配列オブジェクトの要素。さらに、式の場合(P)+N
N+(P)
(P)-N
N
P
配列オブジェクトの最後の要素を指す場合、式は配列オブジェクトの最後の要素の 1 つ後ろを指し、式が配列オブジェクトの最後の要素の 1 つ後ろを指す(P)+1
場合、式は配列オブジェクトの最後の要素を指します。ポインターオペランドと結果の両方が同じ配列オブジェクトの要素を指している場合、または配列オブジェクトの最後の要素の 1 つ後ろを指している場合、評価はオーバーフローを生成しません。それ以外の場合、動作は未定義です。Q
(Q)-1
したがって、存在する場合p + 19
の要素 19 を指します。a[0]
ただしa[0]
、 は 5 つの要素の配列であるため、要素 19 は存在しないため、 の動作はp + 19
標準で定義されていません。
同様に、q + -1
は の要素 -1 を指しますa[1]
が、要素 -1 は存在しないため、 の動作はq + -1
標準で定義されていません。
これらの配列がより大きな配列に含まれていること、およびこのより大きな配列内のすべての要素のメモリ レイアウトがわかっていることは問題ではありません。C 標準では、より大きなメモリ レイアウトに関して動作が定義されていません。ポインター演算が評価される特定の配列に基づいて動作を指定します。AC 実装では、この算術を単純なアドレス算術のように機能させ、必要に応じて動作を定義することは自由ですが、これを行わないことも許可されていました。コンパイラの最適化は年々より洗練され積極的になってきており、メモリ レイアウトに関係なく、特定の配列演算に関する C 標準の規則に基づいてこれらの式を変換する可能性があり、これにより式が失敗する可能性があります (単純なアドレス演算)。