わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

「char *型」と言えば簡単なのに

昨日の授業は,Cの難関,ポインタを紹介しました.
昨年の授業資料(PowerPointファイル)を見直していた中で,

char *p; と書けば,「char *」型の変数pを宣言する

と書いていたのですが,Webの情報や,手元の何冊かの本を読んだところ,この表現は妥当でなく,規格に基づくなら

char *p; と書けば,char型を指し示すためのポインタ変数pを宣言する

とすべきようです.(ついでに,宣言時の「*」は,ポインタ宣言子と呼ばれる記号であり,演算子ではありません.)
少し考えましたが,今年のスライドについては,まず「char型を指し示すためのポインタ変数p」を書き,次に「この授業では」と断り書きをつけて,「char *型」という表記や呼称を使っていくことにしました.
「char *型」という表現を採用することのメリットを並べてみますと

  • データ型を言うときに,「charへのポインタ」よりも「char *」のほうが簡潔ですし,ASCII文字だけで書けます.
  • 「変数名」と「型名」を分離して説明できます*1.分離というよりは,変数は「値」と「型」を持つ,と考えるのがいいかもしれません(もちろんこれは,『物理量を表現するときは、数と単位を組にしてかきあらわす』(物理量 - Wikipedia)のアナロジーです).

これらのメリットを使うと,多次元配列をポインタ変数で指し示す*2ときに,ポインタ変数をどう宣言すればいいかが,説明しやすくなります.
具体例を挙げてみますと,char a[2][3]; に対して,「p = a;」とするためにどんなポインタ変数を宣言すればいいか…
答えは「char (*p)[3];」です.
理由ですが,配列変数aは,配列としてはchar [2][3]型,そしてaをポインタ値としてみたときの型はchar *[3]…と書きたいけれど*3,構文としては「char (*)[3]」*4となって,ここに変数名を書くと,「char (*p)[3];」となるわけです.
これは一次元配列でも同様に考えることができます.char b[2]; に対して,「char *q = b;」と書けますが,これは,bはchar [2]型であり,このポインタ値の型はchar (*)…これはカッコを書かないのが自然で「char *」となるからです.
(追記)*5 「char *型」のように表記する実用上のメリットを,思い出しました.

  • ポインタ値をキャストするときの型名として使用します.例えば int *p = (int *)malloc(sizeof(int) * 10); のように書くときの,mallocの直前にある「int *」です.

*1:ポインタ変数に限らず,例えば,関数の仮引数でときどき使う「const char *p」というのは,私は「const char *」(こんすと・ちゃー・ほし)型の変数p,と説明しています.

*2:授業で軽く説明していますが,1年生に教えるには難しすぎる上に重要度も低いです.多次元配列を関数の引数にしたいときは,仮引数を,配列形式で表現すればいいですし.

*3:「char *p[3];」と書くと,「char *」型の値を3つ持つ配列変数pを宣言することになります.

*4:「char [3]」という配列型を指し示すためのポインタ型です.

*5:ここより前については,木曜の晩に書いていました.追記のことを思い出したのは,昨日の昼食中でした.