わさっきhb

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

自己参照構造体

自己参照構造体で,授業中に話したことを思い出す….
自己参照構造体を定義するとき,ダメな書き方が2種類かあります.いずれもコンパイラが検出して,エラーを報告します.

/* ダメな書き方1 */
struct {
  int value;
  node *next;
} node;
/* ダメな書き方2 */
struct node {
  int value;
  struct node next;
};
node n;

(なお,「int value;」は,アプリケーションによってもっと複雑になりますし,ここに構造体を書くこともあります.valueは「値」,nodeはグラフ理論でいう「節点(せってん)」という意味です.)
ダメな書き方1がダメなのは,コンパイラソースコードを上から読んでいって,「node *next;」を見つけたときに,型名であるべきnodeが定義されていないからです.
ダメな書き方2がダメなのは,この構造体をメモリ上で表現できないからです.変数nを一つの箱として,その箱の中身がどうなるかを描くことができません(node構造体の中にnode構造体があって,その中にnode構造体があって,…,無限に構造体ができる!?).別の言い方をすると,sizeof(n)が求められません.
正しい書き方は,こうです.

/* 正しい書き方1 */
struct node {
  int value;
  struct node *next;
};

自己参照構造体を構成できるのは,メンバnextの型が(自己参照する)構造体そのものではなく,そのポインタだからです.
専門用語を用いると,nextの型を決定する時点では「struct node」が「不完全型」として利用できます.そして型定義の文を終えたところで,「struct node」は「構造体型」とみなされます.
不完全型は,その内部構造(メモリにどのように表現するか)が決まっていません.なので,sizeofを使ってそのメモリサイズを求めることはできません.ですが,そのポインタであれば,要はアドレスですので,sizeof(char *)もsizeof(int *)もsizeof(struct node *)も同じになります.結局,不完全型のポインタは利用しても差し支えないわけです.
実際には,次のように書くことが多いでしょう.

/* 正しい書き方2 */
typedef struct node {
  int value;
  struct node *next;
} node;
node n;

これは「struct node型」と「node型」が定義されます.もう一つ,変数を用意して,「node m = {1, &n};」と書くことができます(ただし,nもmもauto変数としておきます).m.nodeは「struct node *型」,&nは「node *型」だから異なる型,ではありません.typedefのおかげで,これらは同一の型とみなされます.
2008年6月3日追記: 正しい書き方2について,構造体のタグ名と,typedefで定義する型名が,ともにnodeになっていますが,これらを違うものにするほうが,より安全です.1990年代のCプログラミング - わさっきを参照ください.