/*
配列関係
 */

#include<stdio.h>

/* 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 などを使う方が分かりやすくていい。
 */
