前回は多次元配列にポインタでアクセスする方法や,動的に確保したメモリーをポインタで多次元配列のように扱う方法を説明しました。malloc関数*1などで動的に確保したメモリー上の固まり(メモリー・ブロック)をどう扱うかは,ポインタの宣言次第であることを見ていただきました。ポインタの宣言が難しそうに見えても,実際にメモリーに値が格納されている仕組みは単純なのだと感じていただけたでしょうか。
復習を兼ねて,一つサンプル・プログラムを見てみましょう。リスト1は,2次元配列をポインタで扱うプログラムです。リスト1の(1)で,
char (*p)[10];
とポインタを宣言しました。これは配列のポインタ,正確に書けば「char型の要素を10個持つ配列へのポインタ」の宣言です。(2)のfor文に登場するポインタ*(p+i)はそれぞれ,str[0],str[1],str[2]を指します。
これとよく似たポインタの宣言に
char *p[10];
があります。これは「ポインタの配列」の宣言です。10回目となる今回はポインタの配列からお話をはじめます。ほかにポインタのポインタ,構造体などについてもお話しします。どうぞゆっくり,一つひとつ確かめながら学んでくださいね。
配列の形式でポインタを定義できる
リスト1と同じくFebruary(2月),March(3月),April(4月)と表示するプログラムをポインタの配列を使って書いてみました(リスト2)。
出力結果はリスト1ともちろん同じです(図1)。コードもよく似ていますね。でも仕組みは全く違うのです。リスト1ではchar str[3][10]で2次元配列を定義して,文字列を代入しています。その2次元配列を「char型の要素を10個持つ配列」へのポインタpで操作しています。pは単に一つのポインタ変数です。
それに対してリスト2では,要素を三つ持つポインタの配列をchar *str[3]で定義しています(リスト2の(3))。つまり,配列の形式でポインタを三つ定義しているのですね。February,March,Aprilの各文字列はchar *str[3]とは別のメモリーのどこかに確保され,ポインタ配列の1個目の要素str[0]にFebruaryの先頭アドレスが,2個目の要素str[1]にMarchの…,と各文字列の先頭アドレスが格納されます(図2)。
リスト2のprintf関数の部分を,
printf("%p:%s\n", str[i],str[i]);
のように,変換仕様%pに文字列の先頭アドレスを渡して表示するプログラムに変更した実行結果が図3です。
ポインタ配列str[3]の中には,それぞれの月を表す英文字列を指すポインタが入っているのです。理解できましたか? これを踏まえてポインタのポインタへ進みましょう。