Namespaces
Variants

Lookup and name spaces

From cppreference.net

C 프로그램에서 identifier 가 발견되면, 해당 identifier를 도입한 declaration 이면서 현재 scope 내에 있는 선언을 찾기 위한 조회가 수행됩니다. C는 이러한 identifier들이 서로 다른 범주에 속하는 경우, 즉 name space 라 불리는 경우 동일한 identifier에 대한 여러 선언이 동시에 scope 내에 존재하는 것을 허용합니다:

1) 레이블 이름 공간: 모든 식별자가 레이블 로 선언됨.
2) 태그 이름: structs , unions enumerated types 의 이름으로 선언된 모든 식별자. 세 종류의 태그가 모두 하나의 네임스페이스를 공유한다는 점에 유의하십시오.
3) 멤버 이름: 모든 식별자는 하나의 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/union/enum 이름을 일반 식별자의 네임스페이스에 주입하는 것이 일반적인 관행입니다:

struct A { };       // 태그 이름 공간에 이름 A를 도입
typedef struct A A; // 먼저, "struct" 뒤의 A에 대한 조회가 태그 이름 공간에서 하나를 찾음
                    // 그런 다음 일반 이름 공간에 이름 A를 도입
struct A* p;        // OK, 이 A는 태그 이름 공간에서 조회됨
A* q;               // OK, 이 A는 일반 이름 공간에서 조회됨

동일한 식별자가 두 개의 네임스페이스에서 사용되는 잘 알려진 예시는 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++에서는 오류

표준 속성, 속성 접두사 또는 비표준 속성 이름이 지원되지 않는 경우, 잘못된 속성 자체는 오류를 발생시키지 않고 무시됩니다.

(since C23)

예제

void foo (void) { return; } // 일반 네임스페이스, 파일 스코프
struct foo {      // 태그 네임스페이스, 파일 스코프
    int foo;      // 이 struct foo에 대한 멤버 네임스페이스, 파일 스코프
    enum bar {    // 태그 네임스페이스, 파일 스코프
        RED       // 일반 네임스페이스, 파일 스코프
    } bar;        // 이 struct 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++ 문서 참조: Name lookup