C言語の配列とか添え字演算子とかポインタとか

どうもポインタの理解が甘いので復習してみたが、訳が分からない。ポインタのポインタ(ダブルポインタ)とかポインタのポインタのポインタ(トリプルポインタ)とかイミフ。

以下、ソースコード

#include <stdio.h>
#include <stdlib.h>

typedef struct aa
{
	char c;
	struct aa *a;
} aa;

int main(void)
{
	int i[]       = {0, 1, 2, 3};
	int *ip       = i;
	int **ipp1    = &ip;
	int **ipp2[2] = {&ip, };

	int *j = malloc(sizeof(int) * 4);
	j[0] = 900;
	j[1] = 8000;
	j[2] = 70000;
	j[3] = 600000;

	int **jpp = malloc(sizeof(int *) * 4);
	jpp[0] = j + 3;
	jpp[1] = j + 2;
	jpp[2] = j + 1;
	jpp[3] = &j[0];

	int ***jppp = &jpp;

	aa z = {'z', NULL};
	aa *a        = malloc(sizeof(aa));
	aa *ap       = a;
	aa **app1    = &ap;
	aa **app2[2] = {&ap};
	aa **app3    = malloc(sizeof(aa *) * 3);
	a->a         = malloc(sizeof(aa));
	a->a->a      = NULL;
	app2[1] = &(a->a);
	app3[0] = NULL;
	app3[1] = ap;
	app3[2] = &z;
	a->c    = 'l';
	a->a->c = 'Q';

	printf("i[0]\t\t == %d\n",            i[0]);
	printf("ip[1]\t\t == %d\n",           ip[1]);
	printf("*(ip + 2)\t\t == %d\n",       *(ip + 2));
	printf("**ipp1\t\t == %d\n",          **ipp1);
	printf("*(*ipp1 * 2)\t\t == %d\n",    *(*ipp1 + 2));
	printf("*(*ipp2[0] + 3)\t\t == %d\n", *(*ipp2[0] + 3));

	puts("");

	printf("***jppp\t\t == %d\n",       ***jppp);
	printf("**(jppp[0])\t\t == %d\n",   **(jppp[0]));
	printf("**(*jppp + 1)\t\t == %d\n", **(*jppp + 1));
	printf("*(jppp[0][2])\t\t == %d\n", *(jppp[0][2]));
	printf("jppp[0][3][0]\t\t == %d\n", jppp[0][3][0]);

	puts("");

	printf("j\t\t == %p\n",          j);
	printf("&j\t\t == %p\n",         &j);
	printf("jpp\t\t == %p\n",        jpp);
	printf("*(jpp + 3)\t\t == %p\n", *(jpp + 3));
	printf("&jpp\t\t == %p\n",       &jpp);
	printf("jppp\t\t == %p\n",       jppp);
	printf("&jppp\t\t == %p\n",      &jppp);

	puts("");

	puts("aはポインタ");
	printf("a->c\t\t == %c\n",    a->c);
	printf("a->a->c\t\t == %c\n", a->a->c);
	printf("a->a->a\t\t == %p\n", a->a->a);

	puts("");

	puts("apはポインタ");
	printf("(*ap).c\t\t == %c\n",      (*ap).c);
	printf("(&(*ap))->c\t\t == %c\n",  (&(*ap))->c);
	printf("(*ap).a->c\t\t == %c\n",   (*ap).a->c);
	printf("(*(ap->a)).a\t\t == %p\n", (*(ap->a)).a);
	printf("(*(*ap).a).c\t\t == %c\n", (*(*ap).a).c);

	puts("");

	puts("app1はポインタのポインタ");
	printf("(*(*app1)).c\t\t == %c\n",      (*(*app1)).c);
	printf("(*app1)->c\t\t == %c\n",        (*app1)->c);
	printf("(*(*app1)).a->c\t\t == %c\n",   (*(*app1)).a->c);
	printf("(*(*app1)->a).c\t\t == %c\n",   (*(*app1)->a).c);
	printf("(*app1)->a->c\t\t == %c\n",     (*app1)->a->c);
	printf("((**app1).a)->a\t\t == %p\n",   ((**app1).a)->a);
	printf("(*(*(*app1)).a).c\t\t == %c\n", (*(*(*app1)).a).c);

	puts("");

	puts("app2はポインタのポインタの配列");
	printf("(*(*app2[0])).c\t\t == %c\n",          (*(*app2[0])).c);
	printf("(*app2[0])->c\t\t == %c\n",            (*app2[0])->c);
	printf("(*(*app2[0])).a->c\t\t == %c\n",       (*(*app2[0])).a->c);
	printf("(*(*app2[0])->a).c\t\t == %c\n",       (*(*app2[0])->a).c);
	printf("(app2[0][0]->a)->a\t\t == %p\n",       (app2[0][0]->a)->a);
	printf("(*(app2[0][0]->a)).a\t\t == %p\n",     (*(app2[0][0]->a)).a);
	printf("(*((*(app2[0][0])).a)).a\t\t == %p\n", (*((*(app2[0][0])).a)).a);
	printf("(**app2)->c\t\t == %c\n",              (**app2)->c);
	printf("(**(app2 + 1))->c\t\t == %c\n",        (**(app2 + 1))->c);
	printf("(***app2).c\t\t == %c\n",              (***app2).c);
	printf("(***(app2 + 1)).c\t\t == %c\n",        (***(app2 + 1)).c);
	printf("(***app2).a->a\t\t == %p\n",           (***app2).a->a);
	printf("*(***app2).a).c\t\t == %c\n",          (*(***app2).a).c);

	puts("");

	puts("app3はポインタのポインタ * 3");
	printf("(*(*(app3 + 1))).c\t\t == %c\n",    (*(*(app3 + 1))).c);
	printf("(**(app3 + 1)).c\t\t == %c\n",      (**(app3 + 1)).c);
	printf("(*(app3 + 1))->c\t\t == %c\n",      (*(app3 + 1))->c);
	printf("(*(app3 + 1))->a->c\t\t == %c\n",   (*(app3 + 1))->a->c);
	printf("(**(app3 + 1)).a->c\t\t == %c\n",   (**(app3 + 1)).a->c);
	printf("(*(**(app3 + 1)).a).a\t\t == %p\n", (*(**(app3 + 1)).a).a);
	printf("(*(*(app3[1])).a).c\t\t == %c\n",   (*(*(app3[1])).a).c);
	printf("(*(app3[1]->a)).c\t\t == %c\n",     (*(app3[1]->a)).c);
	printf("(app3[1][0].a)->c\t\t == %c\n",     (app3[1][0].a)->c);
	printf("app3[2]->c\t\t == %c\n",            app3[2]->c);
	printf("app3[0]\t\t == %p\n",               app3[0]);

	free(a->a);
	free(a);
	free(app3);
	free(j);
	free(jpp);

	return 0;
}

実行するとこうなる。

i[0]		 == 0
ip[1]		 == 1
*(ip + 2)		 == 2
**ipp1		 == 0
*(*ipp1 * 2)		 == 2
*(*ipp2[0] + 3)		 == 3

***jppp		 == 600000
**(jppp[0])		 == 600000
**(*jppp + 1)		 == 70000
*(jppp[0][2])		 == 8000
jppp[0][3][0]		 == 900

j		 == 0x8fb0008
&j		 == 0xbf83cb80
jpp		 == 0x8fb0020
*(jpp + 3)		 == 0x8fb0008
&jpp		 == 0xbf83cb84
jppp		 == 0xbf83cb84
&jppp		 == 0xbf83cb88

aはポインタ
a->c		 == l
a->a->c		 == Q
a->a->a		 == (nil)

apはポインタ
(*ap).c		 == l
(&(*ap))->c		 == l
(*ap).a->c		 == Q
(*(ap->a)).a		 == (nil)
(*(*ap).a).c		 == Q

app1はポインタのポインタ
(*(*app1)).c		 == l
(*app1)->c		 == l
(*(*app1)).a->c		 == Q
(*(*app1)->a).c		 == Q
(*app1)->a->c		 == Q
((**app1).a)->a		 == (nil)
(*(*(*app1)).a).c		 == Q

app2はポインタのポインタの配列
(*(*app2[0])).c		 == l
(*app2[0])->c		 == l
(*(*app2[0])).a->c		 == Q
(*(*app2[0])->a).c		 == Q
(app2[0][0]->a)->a		 == (nil)
(*(app2[0][0]->a)).a		 == (nil)
(*((*(app2[0][0])).a)).a		 == (nil)
(**app2)->c		 == l
(**(app2 + 1))->c		 == Q
(***app2).c		 == l
(***(app2 + 1)).c		 == Q
(***app2).a->a		 == (nil)
*(***app2).a).c		 == Q

app3はポインタのポインタ * 3
(*(*(app3 + 1))).c		 == l
(**(app3 + 1)).c		 == l
(*(app3 + 1))->c		 == l
(*(app3 + 1))->a->c		 == Q
(**(app3 + 1)).a->c		 == Q
(*(**(app3 + 1)).a).a		 == (nil)
(*(*(app3[1])).a).c		 == Q
(*(app3[1]->a)).c		 == Q
(app3[1][0].a)->c		 == Q
app3[2]->c		 == z
app3[0]		 == (nil)