C 言語での名前空間の違いの2006年08月24日 11時05分26秒

あまり深く考えて書いたプログラムではないが、C 言語の名前空間の違いを見せるためのプログラムだ。

#define のマクロ、typedef、struct と enum、変数の間で名前空間が違うために、a や A をいっぱい使っても文法的には問題はない。それでも可読性に欠けるので、一部の例外を除いて、利用は控えるべきだ。


#include <stdio.h>

#define a(x) ((x)->a)

typedef int a;

struct a
{
    int a;
};

int aa()
{
    struct a a = {3};
    printf("%d\n", a(&a));
}

int main()
{
    a a = 2;
    int (*A)() = aa;
    printf("%d\n", a);
    A();
}

#define の a はプリプロセッサによって処理される。それ故、他の名前空間と別なのは理解しやすい。それぞれ、プリプロセッサとコンパイラの処理は別なのだから、交じりようがないわけだ。

struct と enum の名前空間が存在する。struct a と enum a は共存できない。構造体や列挙型は、必ず struct と enum が付くので、変数を定義するときに、必ず区別が付く。


struct a a;

は構造体 a の型の変数 a を定義している。

それに加えて、typedef だ。私もこれは最初、面白い動作だと思った。struct や enum と同じ名前空間だと思っていたからだ。私は struct や enum も typedef を使って定義するのが好きだ。元々 C++ から始めたから、わざわざ struct や enum を付けるのがあまり好きでないようだ。それに、struct や enum がなくても、構造体なのは判るので。

しかし、ポインタ型や配列型に typedef を使うのは C 言語の中でも一番嫌いな部分だ。これらを定義すると、ポインタ型や配列型だという区別がとても付きにくくなる。わざわざ * や [] を付けて、一目瞭然で判るようにしているのに、それがなくなるので可読性が著しく落ちる。

さて、最後の名前空間は変数だ。関数は変数の中に入る。よく知られているが、aa の中には関数の開始番地が保持されている。aa では、定数の様に変更できないが、関数型として定義された A には、関数を代入することで、実行する関数を変更することが出来る。

実行結果だ。さて、思った通りの結果だっただろうか。 a ではなく c を使った方がもっと混乱してよかっただろうか。


% vi a.c; make a; ./a
cc -O2 -fno-strict-aliasing -pipe   a.c  -o a
2
3

なお、同じ名前を違う名前空間で使う例外は以下の様な場合だ。typedef を使いたいときに、すっきりできる。


typedef struct node
{
    void *data;
    struct node *prev;
    struct node *next;
} node;