/* 配列関係 */ #include /* 1次元配列 */ void Array1() { int a[] = {1, 2, 3, 4}; int *p = a; printf("%d, %d\n", a, p); /* ↑ * 配列 a を [] なしで使うと、ポインタとほぼ同じ意味になる。 * * ということは・・・ * ↓みたいなまねができる。 */ printf("%d, %d, %d, %d\n", a[2], *(a + 2), *(2 + a), 2[a]); /* ↑ * 4つとも同じ意味。 * 4つ目は気持ち悪いかもしれないけども、 * C 言語ではこういう書き方が可能。 * * ただし、完全にポインタと同じというわけでもなく・・・ */ /* ++a; a = p; */ /* ↑ * ポインタと違って、代入やアドレスのインクリメントは不可。 * この2行はエラーになるので、コンパイルするためにはコメントアウトが必要。 * */ printf("%d, %d\n", sizeof(a), sizeof(p)); /* ↑ * sizeof の結果も、配列とポインタでは違うので注意。 * sizeof(配列) は sizeof(int) * 配列の要素数 * sizeof(ポインタ) は sizeof(int *) * と同じ意味。 * * ちなみに、このことを利用すると、 * sizeof(a) / sizeof(int) で、配列 a の要素数を得ることが可能。 */ } /* 2次元配列 */ void Array2() { int a[2][2] = { {1, 2}, {3, 4} }; int **p; int *q[2] = {a[0], a[1]}; int *r; printf("%d, %d, %d, %d\n", a[0][0], **a, a[0][1], *(*a + 1) ); printf("%d, %d, %d, %d\n", a[1][0], **(a + 1), a[1][1], *(*(a + 1) + 1) ); /* ↑ * 2次元配列は2重ポインタ扱い * っぽく使えるんだけど・・・ */ /* p = a; printf("%d, %d\n", p[0][0], p[1][1]); */ /* ↑ * 2次元配列を2重ポインタに代入したりはできない。 * この行、コンパイルできることはできるけど、実行するとエラーに。 * エラーなく実行するためにはコメントアウトを。 */ printf("%d, %d\n", q[0][0], q[1][1]); /* ↑ * q みたいにポインタの配列に代入するなら OK。 * これはちゃんと a[0][0], a[1][1] の値を取れる。 */ printf("%d, %d, %d\n", a, a[0], &a[0][0]); printf("%d, %d, %d\n", q, q[0], &q[0][0]); /* ↑ * この行を実行してみれば分かるけども、 * a と a[0] と &a[0][0] がどれも同じアドレスを指す。 * q の方は、q[0] と &q[0][0] は同じだけど、q は別。 * * この辺り、多次元配列は特殊な処理されてるので、 * 多重ポインタと完全に同じ間隔で使うとまずい。 */ r = &a[0][0]; printf("%d, %d, %d, %d\n", r[0], r[1], r[2], r[3]); printf("%d, %d, %d, %d\n", r[0], r[1], r[2 * 1 + 0], r[2 * 1 + 1]); /* ↑ * 多次元配列の各行は、隙間なく繋がって並んでる。 * なので、1次元的にもアクセス可能。 * * ようするに、i 行 j 列目にアクセスするなら、 * r[i * 列数 + j] */ } void FunctionWithArrayParam(int a[]) { printf("%d\n", sizeof(a)); /* ↑ * 配列を引数として使った場合、 * ポインタと同じ性質になってしまう。 * この場合、sizeof(a) は sizeof(配列) ではなく、sizeof(ポインタ) と同じ結果。 * すなわち、sizeof(int *) になる。 * * sizeof(a) / sizeof(int) では配列長を取得できないので、 * 配列長も引数として渡す必要がある。 */ } void FunctionWithMultiDimArray(int a[][]) { /* printf("%d, %d\n", a[0][0], a[0][1]); */ /* ↑ * 実はこれ、コンパイルエラーを起こす。 * * Array2() で、int **p = a; がエラーになったのと同じ理由。 * 引数として配列を使うと、ポインタになってしまう。 * int a[][] なら、int **a とほぼ同じ意味。 * * int a[2][] とすれば正しく動作するようになるけど、 * 配列の幅が 2 の2次元配列しか受け取れなくなる。 */ } /* 関数の引数に配列を使う */ void Array3() { int a[] = {1, 2, 3, 4}; int b[2][2] = {{1, 2}, {3, 4}}; FunctionWithArrayParam(a); FunctionWithMultiDimArray(b); } int main() { Array1(); /* Array2(); Array3(); */ return 0; } /* ・演習 コメントを外すとエラーになる部分のコメントをはずしてみてエラーメッセージを確認してみる Array1 〜 Array3 は、それぞれ別個に実行してみた方が分かりやすいかも。 1つを残してコメントアウト。 malloc で動的に配列を確保したいときには特に注意。 多次元配列は特に特殊なので、 C++ なら vector の vector などを使う方が分かりやすくていい。 */