名前空間
変種

名前探索と名前空間

提供: cppreference.com

C のプログラムで識別子に遭遇すると、現在のスコープ内にあるその識別子を導入した宣言を特定するために名前探索が行われます。 C は、カテゴリ (名前空間と言います) が異なるならば、同じ識別子の複数の宣言がスコープ内にあることを許容します。

1) ラベル名前空間。 ラベルとして宣言されたすべての識別子。
2) タグ名前空間。 構造体共用体列挙型の名前として宣言されたすべての識別子。 この3種類のタグはすべて同じ名前空間を共有します。
3) メンバ名前空間。 構造体また��共用体のメンバとして宣言されたすべての識別子。 すべての構造体および共用体はそれ自身の名前空間を導入します。
4) その他のすべての識別子 (関数名、オブジェクト名、typedef名、列挙定数)。 (1-3) と区別するために普通の識別子と呼びます。

名前探索において、識別子の名前空間は、それが使用された状況によって、以下のように決定されます。

1) goto 文の被演算子として現れた識別子はラベル名前空間で名前探索されます。
2) キーワード structunion、または enum に続いて現れた識別子はタグ名前空間で名前探索されます。
3) メンバアクセス演算子またはポインタを通したメンバアクセス演算子に続いて現れた識別子はそのメンバアクセス演算子の左側の被演算子によって決定される型のメンバの名前空間で名前探索されます。
4) その他のすべての識別子は普通の識別子の名前空間で名前探索されます。

ノート

マクロの名前は意味解析より前にプリプロセッサによって置換されるためいかなる名前空間の一部でもありません。

typedef 宣言を使用して普通の識別子の名前空間に struct/union/enum の名前を注入することは一般的な慣習です。

struct A { };       // タグ名前空間に名前 A を導入します。
typedef struct A A; // まず、 struct の後の A に対する名前探索によりタグ名前空間でそれが発見されます。
                    // その後、普通の名前空間に名前 A を導入します。
struct A* p;        // OK、この A はタグ名前空間で名前探索されます。
A* q;               // OK、この A は普通の名前空間で名前探索されます。

2つの名前空間に渡って使用される同じ識別子の良く知られた例は POSIX のヘッダ sys/stat.h の識別子 stat です。 普通の識別子として使用されたときは関数を表し、タグとして使用されたときは構造体を表します。

C++ と異なり、列挙定数は構造体メンバではなく、その名前空間は普通の識別子の名前空間であり、 C には構造体スコープがないため、そのスコープは構造体の宣言が現れたスコープになります。

struct tagged_union 
{
   enum {INT, FLOAT, STRING} type;
   int integer;
   float floating_point;
   char *string;
} tu;

tu.type = INT; // C では OK、 C++ ではエラー。

void foo (void) { return; } // 普通の名前空間/ファイルスコープ
struct foo {      // タグ名前空間/ファイルスコープ
    int foo;      // 構造体 foo のメンバ名前空間/ファイルスコープ
    enum bar {    // タグ名前空間/ファイルスコープ
        RED       // 普通の名前空間/ファイルスコープ
    } bar;        // 構造体 foo のメンバ名前空間/ファイルスコープ
    struct foo* p; // OK、タグ/ファイルスコープの名前「foo」を使用します。
};
enum bar x; // OK、タグ/ファイルスコープの「bar」を使用します。
// int foo; // エラー、普通の名前空間の「foo」はスコープ内にすでに存在します。
//union foo { int a, b; }; // エラー、タグ名前空間の「foo」はスコープ内に存在します。

int main(void)
{
    goto foo; // OK、ラベル名前空間/関数スコープの「foo」を使用します。

    struct foo { // タグ名前空間/ブロックスコープ (ファイルスコープを隠蔽します)。
       enum bar x; // OK、タグ名前空間/ファイルスコープの「bar」を使用します。
    };
    typedef struct foo foo; // OK、タグ名前空間/ブロックスコープの「foo」を使用し、
                            // 普通/ブロックスコープの「foo」を定義します (ファイルスコープを隠蔽します)。
    (foo){.x=RED}; // 普通/ブロックスコープの「foo」と普通/ファイルスコープの「RED」を使用します。

foo:; // ラベル名前空間/関数スコープ
}


参考文献

  • C11 standard (ISO/IEC 9899:2011):
  • 6.2.3 Name spaces of identifiers (p: 37)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.2.3 Name spaces of identifiers (p: 31)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.1.2.3 Name spaces of identifiers

関連項目

名前探索C++リファレンス