16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ポインタのポインタを理解するための動作確認(メモ)

Last updated at Posted at 2018-11-10

c言語初学者の例にもれずポインタ迷子になりました。

・ポインタのポインタ
・ポインタの配列
・配列のポインタへのキャスト
あたりを勉強し始めたくらいです。
(既に呼び方が合ってるかも怪しい・・・)

参考にしたページ↓
配列とポインタの完全制覇

動作を確認しながら現状の理解をメモ。
(おそらく間違いだらけです。コメントでご指摘もらえるとうれしいです。)

#「配列の配列」とポインタ
以下の配列の配列(多次元配列)とポインタを使っていろいろ動作確認。

	char ss[5][6]	= {"abc", "cdef", "fgh", "hijkl", "lm"};
	char *p  = (char *)ss;

【現状の理解】
ss[5][6]  ・・・要素数が5で、「要素数6のchar型配列」型の配列
*p     ・・・charへのポインタ型
(char *)ss ・・・ssを(charへのポインタ型)にキャスト。つまり&ss[0][0]

以下のプログラムでポインタが保持するアドレスの値を確認

	for(i = 0; i < 10; i++){
		printf("*p + %d = %c    "	, i, *p + i);
		printf("*(p + %d) = %c"		, i, *(p + i));
	}
実行結果
*p + 0 = a    *(p + 0) = a
*p + 1 = b    *(p + 1) = b
*p + 2 = c    *(p + 2) = c
*p + 3 = d    *(p + 3) = 
*p + 4 = e    *(p + 4) = 
*p + 5 = f    *(p + 5) = 
*p + 6 = g    *(p + 6) = c
*p + 7 = h    *(p + 7) = d
*p + 8 = i    *(p + 8) = e
*p + 9 = j    *(p + 9) = f

結果から、
・*p は、s[0][0]の先頭アドレスの値なので'a'
・*p + 1 は、'a' + 1 と同じことを指している。つまり*pはchar型。
・*(p + i)も、もちろんchar型
・*(p + i)は ポインタ*pの先頭アドレスからi個進んだ先頭アドレスを指したうえで、その値を示している(値はchar型)
・ポインタの示す先頭アドレスに「*」をつけると、そのアドレスの値を示す

ついでに、サイズも確認

	printf("sizeof(*p) = %u\n", (unsigned)sizeof(*p));
	printf("sizeof( p) = %u\n\n", (unsigned)sizeof(p));

実行結果
sizeof(*p) = 1
sizeof( p) = 8

・*pはchar型なので、1byte
・ pはアドレスを格納しているので、アドレスのサイズが表示される。(アドレスの中身の型は無関係)この処理系ではアドレスは8byte表示

#ポインタの配列
今度はポインタの配列を使って動作確認

	char *tt[5]		= {"ABC", "CDEF", "FGH", "HIJKL", "LM"};
	char **q = tt;

【現状の理解】
*tt[5] ・・・charへのポインタ型の配列。要素数は5
**q ・・・charへのポインタへのポインタ型

以下のプログラムでアドレスやら値やらを確認。

	for(i = 0; i < 5; i++){
		printf("q   + %d = %p    "			, i, q + i);
		printf("*q  + %d = %5s    "			, i, *q + i);
		printf("*(q + %d) = %5s    "		, i, *(q + i));
		printf("*(q + %d) + %d = %5s    "	, i, i, *(q + i) + i);
		printf("**q + %d = %c    "			, i, **q + i);
		printf("*(*(q + %d) + %d) = %c"		, i, i, *(*(q + i) + i));
		putchar('\n');
	}
実行結果
q + 0 = 000000000061FDE0    *q + 0 =   ABC    *(q + 0) =   ABC    *(q + 0) + 0 =   ABC    **q + 0 = A    *(*(q + 0) + 0) = A
q + 1 = 000000000061FDE8    *q + 1 =    BC    *(q + 1) =  CDEF    *(q + 1) + 1 =   DEF    **q + 1 = B    *(*(q + 1) + 1) = D
q + 2 = 000000000061FDF0    *q + 2 =     C    *(q + 2) =   FGH    *(q + 2) + 2 =     H    **q + 2 = C    *(*(q + 2) + 2) = H
q + 3 = 000000000061FDF8    *q + 3 =          *(q + 3) = HIJKL    *(q + 3) + 3 =    KL    **q + 3 = D    *(*(q + 3) + 3) = K
q + 4 = 000000000061FE00    *q + 4 =  CDEF    *(q + 4) =    LM    *(q + 4) + 4 = s[5][6]:     **q + 4 = E    *(*(q + 4) + 4) = s

実行結果の考察
・q + i は、ポインタのポインタが指す先頭アドレスからi個すすんだところのアドレス(格納されている値がアドレスなので、8byteずつすすむ)
・*q + i は、ポインタのポインタが指すアドレスからi個すすんだところのアドレスの中身(qはcharへのポインタ型なので、+1すると1byteすすむ。)
・%s は、指定されたアドレスの文字から、終端文字までの文字を出力する
・*q + 3 は終端文字、*q + 4 は次の文字列になっているということは、文字列のサイズはバラバラになっている。もし各文字列のサイズが同じなら、*q + 4 にも終端文字が格納されているはず。(配列では空いたところにはNULL文字が入る)
・ポインタには「要素数」という概念がない。
(q + i)は、ポインタのポインタが指す先頭アドレスからi個すすんだところのアドレス中身(初期化の時点でq + 4 まではアドレスを格納してある)
・**q + i は、ポインタのポインタが指すアドレスの中身の中身ニ +1した値。(**qは純粋なchar型の値なので、文字コードに従った出力になる)
・文字列中の'a'と文字コードの'a'の指すアドレスは違う。(これ重要!)

サイズは以下のような感じ

	printf("sizeof(q)   = %u\n", (unsigned)sizeof(q));
	printf("sizeof(*q)  = %u\n", (unsigned)sizeof(*q));
	printf("sizeof(**q) = %u\n\n", (unsigned)sizeof(**q));
実行結果
sizeof(q)   = 8
sizeof(*q)  = 8
sizeof(**q) = 1

q・・・charへのポインタへのポインタ型なので先頭アドレスのサイズを指す。
*q・・・charへのポインタのポインタ型の中身(*)なので、中身のcharへのポインタ型の先頭アドレスのサイズを指す。
**q・・・charへのポインタのポインタ型の中身(*)の中身(*)なので、中身のcharへのポインタ型の先頭アドレスの中身の値(char型)のサイズを指す。

ああ、日本語が崩壊していく・・・

#配列とポインタについてのまとめ(現状の理解)
配列
・値を格納する(アドレスの書き換えはダメ!)
・要素数がある(その分、要素数の乗算でサイズが大きくなる)
⇒ 自由度が低く、管理コストがかかるが、扱いは簡単なイメージ
(20人を5チームに分けるので4人ずつ整列しなさい!的な)

ポインタ
・アドレスを格納する(つまり占有領域が8バイトしかなくてうれしい!)
・要素数がない(配列のように使う時も、あくまで「アドレスの指定」という概念)
⇒ 自由度が高く、管理コストが少なくてすむが、扱いは難しいイメージ
(とりあえず少人数のチームを何個か作りたいんだよねえ。リーダーはお前!あとは適当にやってちょ~。的な。)

##思った事、メモ
命令単体での動作確認だけじゃ、深い理解にはならないと思った。(もちろんそれもめちゃくちゃ大切だが。)
基礎知識がついたら簡単なアプリケーションを組まないとなあ。

ポインタを勉強して分からなくなるまでの道のりをメモ
・は~ん、ポインタってアドレスを指すのね。以外に簡単じゃん。
・実は配列もアドレスを指してたのね。なるほど。
・あれ、ポインタと配列って同じじゃんか!
・ポインタと配列同じじゃないな・・・違いはアドレスを格納する領域があるか、ないかか。簡単じゃん!
・ポインタのポインタ・・・ポインタの配列・・・なんだか気分が悪くなってきた。オェッ。
・ポインタのサイズっていつも8byte。配列と違って「要素数」とかいう概念もないのね。
・は~ん、ポインタってアドレスを指すのね。なんとなく分かった・・・気がする・・・【←イマココ】

16
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?