int a[10][5];
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
cout << i[j[a]];
cout << j[i[a]];
}
}
編集:値がすでに配列に初期化されていると仮定し、このcoutは有効ですか?
i[j[a]];
私が懸念しているプログラムに関係なく、その部分だけを説明してください。
int a[10][5];
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
cout << i[j[a]];
cout << j[i[a]];
}
}
編集:値がすでに配列に初期化されていると仮定し、このcoutは有効ですか?
i[j[a]];
私が懸念しているプログラムに関係なく、その部分だけを説明してください。
cout << i[j[a]];
cout << j[i[a]];
C ++では、X[Y]
表記は字句的に*(X+Y)
次のようになります。
[C++11: 5.2.1/1]:
[..]式E1[E2]
は(定義上)*((E1)+(E2))
[..]と同じです。
これは、加算が可換であるためと同じである、とY[X]
同等であることを意味します。*(Y+X)
*(X+Y)
何らかの理由で、作者はこの紛らわしいコードを書くことによって「賢い」ことを試みることに決めました。
可換性とX[Y]
単独の定義による:
i[j[a]] => i[*(j+a)] => *(i+*(j+a)) => *(*(a+j)+i) => *(a[j] + i) => a[j][i]
j[i[a]] => j[*(i+a)] => *(j+*(i+a)) => *(*(a+i)+j) => *(a[i] + j) => a[i][j]
したがってcout
、ステートメントは次のようになります。
cout << a[j][i];
cout << a[i][j];
5
いずれの場合も、配列の各要素には整数しかないため、ループは配列の境界を超えて読み取ろうとしますがa
、内側のループはまで行こうとします10
。
これの実際の結果は定義されていないので、サイレント成功、サイレント失敗、任意の値、セグメンテーション違反、パワーカット、ブラックホール、クリスマスのポニー、つま先の切断、または新しいバイクを取得できます。
ループ条件が修正されている場合でも、最初のステートメントが意味的に正しくないことに注意してください(最初の次元を外側のループに、2番目の次元を内側のループに割り当て続けると仮定します)。
C配列には奇妙な癖があり、「反対」方向からアクセスできます。これは、配列のポインタ演算に深く根ざしています。たとえば、a[1]
はと同等*(a + 1)
です。同様に、1[a]
と同等*(1 + a)
です。加算の可換性により、これは非常にうまく機能します。詳細については、こちらをご覧ください。
その知識をそのままにして、表現i[j[a]]
を2つの異なる部分に分けることができます。j[a]
は*(j + a)
、使用している配列の多次元的な性質により、別の配列を返すのと同じです。たとえば、この返された配列をこのように呼びますp
。i[p]
次に、と同等のステートメントがあります*(i + p)
。すべてを元に戻すと、i[j[a]]
と同等であることがわかります*(i + *(j + a))
。確かに、これはそれi[j[a]]
が単なる難読化された書き方であることを意味しa[j][i]
ます。
C / C ++では、ポインターを整数インデックスで下付き文字にするか、整数インデックスをポインターで下付き文字にすることができ、意味(セマンティクス)はまったく同じです。なぜ非常識な構文が有効なのかはわかりませんが、有効であり、明らかにそうです。
#include <stdio.h>
int main(int argc, char** argv)
{
int i = 1, array[10];
printf("%ld\n", &(i[array])-&(array[i]));
return 0;
}
出力:
0