Namespaces
Variants

Lookup and name spaces

From cppreference.net

Cプログラムで 識別子 が出現すると、その識別子を導入した 宣言 で現在 スコープ内 にあるものを検索するルックアップが実行されます。C言語では、これらの識別子が異なるカテゴリ( 名前空間 と呼ばれる)に属する場合、同じ識別子に対する複数の宣言が同時にスコープ内に存在することを許可しています:

1) ラベル名前空間: labels として宣言されたすべての識別子。
2) タグ名: structs unions および enumerated types の名前として宣言されたすべての識別子。なお、これら3種類のタグはすべて単一の名前空間を共有することに注意。
3) メンバー名: 任意の1つの struct または union のメンバーとして宣言された全ての識別子。各structとunionはこの種類の独自の名前空間を導入します。
4) グローバル属性名前空間: attribute tokens 標準で定義されているもの、または実装定義の属性プレフィックス。
5) 非標準属性名: 属性プレフィックスに続く属性名。各属性プレフィックスは、それが導入する実装定義属性に対して個別の名前空間を持つ。
(C23以降)
6) その他のすべての識別子( 通常識別子 と呼び、 (1-5) と区別する)(関数名、オブジェクト名、typedef名、列挙定数)。

識別子の名前空間は、その使用方法によって、参照時点で決定されます:

1) goto文 のオペランドとして現れる識別子は、ラベル名前空間で検索されます。
2) キーワード struct union または enum に続く識別子は、タグ名前空間で検索されます。
3) メンバアクセス 演算子またはポインタを介したメンバアクセス演算子の後に続く識別子は、メンバアクセス演算子の左側のオペランドによって決定される型のメンバの名前空間で検索されます。
4) 属性指定子内に直接現れる識別子 ( [ [ ... ] ] ) は、グローバル属性名前空間で検索されます。
5) 属性プレフィックスの後に続く :: トークンに続く識別子は、属性プレフィックスによって導入された名前空間で検索されます。
(C23以降)
6) その他のすべての識別子は、通常の識別子の名前空間で検索されます。

目次

注記

マクロの名前は、意味解析の前にプリプロセッサによって置換されるため、どの名前空間にも属しません。 マクロ の名前は、意味解析の前にプリプロセッサによって置換されるため、どの名前空間にも属しません。

構造体/共用体/列挙型の名前を通常の識別子の名前空間に注入する一般的な手法は、 typedef 宣言を使用することです:

struct A { };       // タグ名前空間に名前Aを導入
typedef struct A A; // まず、"struct"の後のAのルックアップでタグ名前空間内の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;
   union {
      int integer;
      float floating_point;
      char *string;
   };
} tu;
tu.type = INT; // Cでは有効、C++ではエラー

標準属性、属性プレフィックス、または非標準の属性名がサポートされていない場合、無効な属性自体はエラーを引き起こさずに無視されます。

(C23以降)

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:; // ラベル名前空間、関数スコープ
}

参考文献

  • C17規格 (ISO/IEC 9899:2018):
  • 6.2.3 識別子の名前空間 (p: 29-30)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.2.3 識別子の名前空間 (p: 37)
  • C99規格 (ISO/IEC 9899:1999):
  • 6.2.3 識別子の名前空間 (p: 31)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 3.1.2.3 識別子の名前空間

関連項目

C++ documentation for Name lookup