[気合で理解する C 言語ポインタ入門]不完全型

久しぶりに書き足します.

今度は不完全型について解説します.
不完全型は,型の定義が不完全でありポインタの取得のみが可能なデータ型のことです.実際にこれがどういうものかというと,たとえば構造体のタグ名のみを宣言したもの:

struct incomplete; // 不完全型 struct incomplete の宣言
struct incomplete i; // コンパイルエラー
struct incomplete* p; // OK

などがあたります.ただし,関数はこれに含まれません.

これがなぜ不完全なのかというと,コンパイラがこのデータ型のデータを格納するのに必要な領域の大きさを知らない(または知ることができない),つまりその型の情報が不完全であるからです.変数(または構造体のメンバ変数)として宣言するためにはコンパイラがそのデータ型の大きさ(や,そのメンバの配置)を知っていなければなりません.言葉を変えると,コンパイラがデータ型の中身の情報を知ることができないならば,プログラマ(というかコンパイラ)がそのデータ型を直接扱うことができないのです.

不完全型という言葉は,C 出のプログラミングになれていないときは記述ミスやヘッダのインクルード忘れによって,コンパイル時の警告やエラーとともに現れることが多いです.もし,警告やエラーメッセージで見た場合は,上のことを頭においてソースコードをよく読んでみるべきです.

不完全型の説明はとっくに終わっていますが,少し現実的な話をしておきます.

たとえば,「スタック」の概念を実装することにしたとしましょう.スタックをここでは先入れ後出しできる int の並びのことだとします.そうすると int のスタックという概念を実装するにはさまざまな方法がありますが,メジャーなやりかたとしては,単純に配列にしてしまうことや,あるいはポインタを用いた単方向リストにしてしまうことがあげられます.
しかし,ユーザ(この「スタック」を使う者)にとっては int を先入れ後出しできればよいわけで,中身が何であろうと知ったことではありません.逆に実装する側から言うと,「スタックを操作するためのインターフェイス」さえ公開すればよく,ユーザが中身を勝手に弄繰り回してくれると困るわけです.
前置きが長くなりましたが,このようなときに不完全型を使います.

ライブラリ開発者(実装する側)は公開するヘッダファイルに次のような宣言を書いておき,ユーザ(スタックの使用者)はこの宣言さえ知っていれば必要な機能にアクセスすることができます.また,宣言されている struct mystack の中身はスタックの実装にとって非常に重要な情報を含んでいるため,ユーザが勝手に弄繰り回す必要はありませんし弄繰り回してはなりません.だから不完全な型宣言でユーザがそうできないようにするのです.

typedef struct mystack* mystack_t;

mystack_t mystack_create( void );
void mystack_push( mystack_t st, int item );
int mystack_pop( mystack_t st );
void mystack_destroy( mystack_t st );

このような工夫により,ユーザはもうひとつの恩恵を得られます.かりに mystack 構造体メンバに変更が加えられたとしても,それに伴ってユーザがこのライブラリに依存するすべてのソースを再コンパイルする手間がなくなる,つまり,変更に対する再コンパイルの時間が減少するのです.

# こういった工夫をコンパイラファイアウォールといったりもします.

なお, mystack 構造体を(不完全型とせず)そのまま使う実装の場合,構造体のサイ
ズが変更されると,依存するあらゆる部分で必要とするメモリのサイズが変わるため,コンパイラが変数を再配置しなければならなくなります.

カテゴリー: C 言語ポインタ入門 パーマリンク

コメントを残す