C ++ 식별자에서 밑줄을 사용하는 것에 대한 규칙은 무엇입니까?


930

C ++에서는 지역 변수 또는 매개 변수가 아닌 구성원 변수라는 사실을 나타 내기 위해 접두어를 사용하여 구성원 변수의 이름을 지정하는 것이 일반적입니다. MFC 배경에서 온 경우 아마도을 사용 m_foo합니다. 나는 또한 myFoo때때로 보았다 .

C # (또는 .NET)은 밑줄 만 사용하는 것이 좋습니다 _foo. 이것이 C ++ 표준에서 허용됩니까?


3
그에 대한 glibc는 매뉴얼 페이지에서 찾을 수 있습니다 gnu.org/software/libc/manual/html_node/Reserved-Names.html 편집 : 참조 opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB

6
이러한 규칙을 무지한다고해서 코드가 컴파일되거나 실행되지 않는다는 것을 의미하지는 않지만 코드가 다른 컴파일러 및 버전으로 이식되지 않을 가능성이 높습니다. 충돌합니다. 이것을 뒷받침하기 위해 나는 어디에서나 대문자로 명명 규칙으로 사용 된 중요한 시스템의 특정 구현을 알고 있습니다. 이로 인해 오류가없는 곳이 있습니다. 물론 나쁜 습관입니다.
g24l

답변:


852

규칙 (C ++ 11에서는 변경되지 않음) :

  • 구현 매크로 로 사용하는 것을 포함하여 모든 범위에서 예약됩니다 .
    • 밑줄로 시작하고 바로 대문자가 나오는 식별자
    • 인접한 밑줄 (또는 "이중 밑줄")을 포함하는 식별자
  • 글로벌 네임 스페이스에 예약 됨 :
    • 밑줄로 시작하는 식별자
  • 또한 std네임 스페이스의 모든 것이 예약되어 있습니다. (그러나 템플릿 전문화를 추가 할 수 있습니다.)

2003 C ++ 표준에서 :

17.4.3.1.2 전역 이름 [lib.global.names]

특정 이름 및 함수 서명 세트는 항상 구현에 예약되어 있습니다.

  • 이중 밑줄 ( __)을 포함하거나 밑줄로 시작하고 대문자 (2.11)로 시작 하는 각 이름 은 모든 사용을 위해 구현에 예약되어 있습니다.
  • 밑줄로 시작하는 각 이름은 전역 네임 스페이스에서 이름으로 사용하기 위해 구현에 예약되어 있습니다. 165

165) 이러한 이름은 네임 스페이스 ::std(17.4.3.1) 에도 예약되어 있습니다 .

C ++는 C 표준 (1.1 / 2, C ++ 03)을 기반으로하고 C99는 표준 참조 (1.2 / 1, C ++ 03)이므로 1999 C 표준에서 적용됩니다.

7.1.3 예약 식별자

각 헤더는 관련 하위 조항에 나열된 모든 식별자를 선언 또는 정의하며, 선택적으로 관련 미래 라이브러리 지침 하위 조항 및 식별자에 항상 나열된 모든 식별자 또는 파일 범위 식별자로 사용하도록 예약 된 식별자를 선언하거나 정의합니다.

  • 밑줄과 대문자 또는 다른 밑줄로 시작하는 모든 식별자는 항상 사용하도록 예약되어 있습니다.
  • 밑줄로 시작하는 모든 식별자는 항상 일반 네임 스페이스와 태그 네임 스페이스 모두에서 파일 범위를 가진 식별자로 사용되도록 예약되어 있습니다.
  • 다음 하위 조항 (향후 라이브러리 지시 사항 포함)의 각 매크로 이름은 연관된 헤더가 포함 된 경우 지정된대로 사용하도록 예약되어 있습니다. 달리 명시되지 않는 한 (7.1.4 참조)
  • 다음 하위 조항 중 하나에서 외부 연계가있는 모든 식별자 (향후 라이브러리 지시 사항 포함)는 항상 외부 연계가있는 식별자로 사용되도록 예약되어 있습니다. 154
  • 다음 하위 조항 중 하나에 나열된 파일 범위가있는 각 식별자 (향후 라이브러리 지시 사항 포함)는 매크로 이름으로 사용되며 연관된 헤더가 포함 된 경우 동일한 네임 스페이스에서 파일 범위가있는 식별자로 예약됩니다.

다른 식별자는 예약되어 있지 않습니다. 프로그램이 예약 된 컨텍스트에서 식별자를 선언하거나 정의하거나 (7.1.4에서 허용하지 않은 경우) 예약 된 식별자를 매크로 이름으로 정의하면 동작이 정의되지 않습니다.

프로그램이 #undef위에 나열된 첫 번째 그룹에서 식별자의 매크로 정의를 (와 함께 ) 제거 하면 동작이 정의되지 않습니다.

154) 외부 링크 예약 식별자리스트가 포함되어 errno, math_errhandling, setjmp, 및 va_end.

다른 제한 사항이 적용될 수 있습니다. 예를 들어 POSIX 표준은 일반 코드에 표시 될 수있는 많은 식별자를 보유합니다.

  • E대문자로 시작하는 이름 은 숫자 나 대문자를 따릅니다.
    • 추가 오류 코드 이름에 사용될 수 있습니다.
  • 소문자로 시작 is하거나 to뒤에 오는 이름
    • 추가 문자 테스트 및 변환 기능에 사용될 수 있습니다.
  • LC_대문자로 시작하는 이름
    • 로케일 속성을 지정하는 추가 매크로에 사용될 수 있습니다.
  • 접미사가 붙 f거나 l예약 된 모든 기존 수학 함수의 이름
    • float 및 long double 인수에서 각각 작동하는 해당 함수의 경우
  • SIG대문자로 시작하는 이름 은 예약되어 있습니다
    • 추가 신호 이름.
  • SIG_대문자로 시작하는 이름 은 예약되어 있습니다
    • 추가 신호 동작.
  • 로 시작하는 이름 str, mem또는 wcs소문자 문자 다음은 예약되어 있습니다
    • 추가 문자열 및 배열 함수
  • 소문자로 시작 PRI하거나 SCN뒤에 오는 이름 또는 X예약 된 이름
    • 추가 형식 지정자 매크로
  • 로 끝나는 이름 _t은 예약되어 있습니다
    • 추가 유형 이름.

현재이 이름을 자신의 목적으로 사용하면 문제가 발생하지 않을 수 있지만 향후 해당 표준 버전과 충돌 할 가능성이 있습니다.


개인적으로 나는 밑줄로 식별자를 시작하지 않습니다. 내 규칙에 새로운 추가 사항 : 밑줄을 거의 사용하지 않으므로 쉽게 이중 밑줄을 사용하지 마십시오.

이 기사에 대한 연구를 한 후에 _t 는 POSIX 표준에 의해 예약되어 있으므로 더 이상 식별자를 끝내지 않습니다 .

로 끝나는 식별자에 대한 규칙은 _t저 를 놀라게했습니다. 나는 그것이 명확하고 공식적인 장과 절을 찾고있는 POSIX 표준 (아직 확실하지 않다)이라고 생각한다. 이것은 예약 된 이름을 나열한 GNU libtool 매뉴얼 에서 가져온 것 입니다.

CesarB는 POSIX 2004 예약 기호 에 대한 다음 링크를 제공했으며 '다른 많은 예약 된 접두사 및 접미사를 찾을 수 있습니다.' POSIX 2008 예약 된 기호는 여기에 정의되어 있습니다. 제한은 위의 제한보다 약간 미묘합니다.


14
C ++ 표준은 C 표준을 "가져 오지"않습니다. 그들은 내가 아는 한 특정 헤더를 가져 오지만 언어 전체 또는 명명 규칙은 아닙니다. 그러나 _t는 저를 놀라게했습니다. 그러나 C이기 때문에 전역 n에만 적용 할 수 있습니다. _t를 클래스 안에서 읽을 때 안전해야합니다
jalf

27
C ++ 표준은 C 표준을 "가져 오지"않습니다. 이 참조 는 C 표준을. C ++ 라이브러리 소개에 "라이브러리는 표준 C 라이브러리의 기능을 사용할 수 있습니다"라고 말합니다. C 표준 라이브러리의 헤더를 적절하게 변경하여 포함 시키지만 "가져 오는"방법은 아닙니다. C ++ 표준에는 예약 된 이름을 설명하는 고유 한 규칙 세트가 있습니다. C로 예약 된 이름을 C ++로 예약해야한다면 이것이 바로 여기에 있습니다. 그러나 C ++ 표준은 그렇게 말하지 않습니다. 그래서 C에 예약 된 것들이 C ++에 예약되어 있다고 생각하지 않습니다.하지만 잘못되었을 수 있습니다.
Johannes Schaub-litb

8
이것이 "_t"문제에 대해 내가 찾은 것입니다. n1256 (C99 TC3)의 말 : "intdef 또는 int로 시작하고 _t로 끝나는 typedef 이름은 예약되어 있습니다." 나는 여전히 "foo_t"와 같은 이름을 사용할 수 있다고 생각하지만 POSIX에 의해 예약 된 것으로 생각합니다.
Johannes Schaub-litb

59
POSIX는 'tolerance'를 'to'+ 소문자로 시작하여 예약합니까? 많은 규칙이이 규칙을 어 기고 있다고 확신합니다!
Sjoerd

23
@LokiAstari, " C ++ 표준은 C 표준으로 정의됩니다. 기본적으로 C ++는 이러한 차이점과 추가 사항이있는 C라고합니다. "넌센스! C ++는 [basic.fundamental] 및 라이브러리의 C 표준 만 참조합니다. 당신이 말하는 사실이라면, 여기서 C는 그런 말 ++ 않습니다 _Bool_ImaginaryC ++에 존재하지 않는? C ++ 언어는 C에 대한 "편집"이 아니라 명시 적으로 정의됩니다. 그렇지 않으면 표준이 훨씬 짧을 수 있습니다!
Jonathan Wakely

198

이름 충돌을 피하는 규칙은 C ++ 표준 (Stroustrup 책 참조)과 C ++ 전문가 (Sutter 등)가 언급 한 것입니다.

개인 규칙

나는 사건을 다루고 싶지 않고 간단한 규칙을 원했기 때문에 간단하고 올바른 개인 을 설계했습니다 .

심볼 이름을 지정할 때 다음과 같은 경우 컴파일러 / OS / 표준 라이브러리와의 충돌을 피할 수 있습니다.

  • 밑줄로 기호를 시작하지 마십시오
  • 안에 두 개의 연속 밑줄이있는 기호의 이름을 지정하지 마십시오.

물론 고유 한 네임 스페이스에 코드를 넣으면 충돌을 피하는 데 도움이됩니다 (그러나 악의적 인 매크로로부터 보호하지는 않음)

몇 가지 예

(매크로는 C / C ++ 기호의 코드 오염이 많기 때문에 매크로를 사용하지만 변수 이름에서 클래스 이름에 이르기까지 다양 할 수 있습니다)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

C ++ 0x 초안에서 추출

로부터 n3242.pdf의 파일 (나는 유사한 것으로 최종 표준 텍스트 예상) :

17.6.3.3.2 전역 이름 [global.names]

특정 이름 및 함수 서명 세트는 항상 구현에 예약되어 있습니다.

— 이중 밑줄 _ _을 포함하거나 밑줄로 시작하고 대문자 (2.12)로 시작하는 각 이름은 사용을 위해 구현에 예약되어 있습니다.

— 밑줄로 시작하는 각 이름은 전역 네임 스페이스에서 이름으로 사용하기 위해 구현에 예약되어 있습니다.

또한 :

17.6.3.3.5 사용자 정의 리터럴 접미사 [usrlit.suffix]

밑줄로 시작하지 않는 리터럴 접미어 식별자는 향후 표준화를 위해 예약되어 있습니다.

글로벌 네임 스페이스에 정의 되지 않은 경우 하나의 밑줄로 시작하고 그 뒤에 소문자가 오는 이름을 고려하지 않는 한이 마지막 절은 혼란 스럽습니다 .


9
@Meysam : __WRONG_AGAIN__두 개의 연속 밑줄 (처음에는 두 개, 끝에는 두 개)이 포함되어 있으므로 표준에 따라 잘못되었습니다.
paercebal

8
@ BЈовић은 : WRONG__WRONG이 잘못 있도록 표준에 따라, 두 개의 연속 밑줄 (중간에 2 개씩) 포함
paercebal

2
고유 한 네임 스페이스에 코드를 넣으면 충돌을 피하는 데 도움이됩니다 . 그러나 식별자가 범위와 상관없이 키워드와 충돌 할 수 있기 때문에 (예 : __attribute__GCC) 여전히 충분하지 않습니다 .
Ruslan

1
표준에 따라 중간 에 두 개의 연속 밑줄이 생기는 데 왜 문제가 있습니까? 사용자 정의 리터럴 접미어는 1234567L또는 4.0f; 와 같은 리터럴 값에 적용됩니다 . IIRC 이것은 ohttp : //en.cppreference.com/w/cpp/language/user_literal을 참조
Jason S

2
Why is there any problem of having two consecutive underscores in the middle according to the standard?표준에 따르면 예약되어 있다고 말합니다. 이것은 좋지 않은 스타일에 대한 조언 이 아닙니다 . 표준 의 결정 입니다. 그들은 왜 이것을 결정 했습니까? 첫 번째 컴파일러는 표준화 전에 이미 이러한 규칙을 비공식적으로 사용했다고 생각합니다.
paercebal

38

에서 MSDN :

식별자의 시작 부분에 두 개의 순차적 밑줄 문자 (__) 또는 대문자로 뒤 따르는 단일 밑줄 문자를 사용하면 모든 범위에서 C ++ 구현을 위해 예약됩니다. 현재 또는 미래의 예약 식별자와 충돌 할 수 있으므로 파일 범위 이름의 경우 밑줄 하나와 밑줄을 사용하지 마십시오.

즉, 소문자가 오는 한 단일 밑줄을 멤버 변수 접두어로 사용할 수 있습니다.

이것은 분명히 C ++ 표준의 17.4.3.1.2 섹션에서 가져 왔지만 온라인으로 전체 표준의 원본을 찾을 수는 없습니다.

이 질문 도 참조하십시오 .


2
"17.6.3.3.2 글로벌 이름"나는 섹션에서 (C + +0 표준의 초안) n3092.pdf에서 유사한 텍스트를 발견
paercebal

7
흥미롭게도 이것은 질문에 대한 직접적이고 간결한 답변이있는 유일한 답변 인 것 같습니다.
hyde

9
@hyde : 사실, 전역 네임 스페이스에 밑줄이있는 식별자가없는 규칙을 건너 뛰기 때문에 그렇지 않습니다. Roger의 답변을 참조하십시오 . C ++ 표준에 대한 권한으로 MS VC 문서의 인용에 대해 매우주의를 기울였습니다.
sbi

@ sbi 나는이 답변에서 "단일 밑줄을 멤버 변수 접두어로 사용하여 소문자가 있는 한 멤버 변수 접두어로 사용할 수 있습니다"라고 언급 했습니다. 텍스트의 벽에.
hyde

5
첫째, 여전히 동일한 규칙이 전역 네임 스페이스에 적용되지 않는다는 힌트가 없다고 생각합니다. 더 나쁜 것은 인접한 밑줄은 식별자 의 시작뿐만 아니라 어디에서나 금지되어 있다는 것 입니다. 따라서이 답변은 사실을 생략하는 것이 아니라 실제로 하나 이상의 적극적으로 잘못된 주장을합니다. 내가 말했듯이 MSVC 문서를 언급하는 것은 VC에 대한 질문이 아니라면 내가하지 않을 것입니다.
sbi

25

질문의 다른 부분은 변수 이름 의 끝에 밑줄을 두어 내부와 충돌하지 않는 것이 일반적 입니다.

클래스와 네임 스페이스에서도이 작업을 수행합니다. "글로벌 범위의 이름 끝과 다른 곳의 이름 시작"과 비교할 때 하나의 규칙 만 기억하면됩니다.


2

그렇습니다. 밑줄은 식별자의 어느 곳에 나 사용될 수 있습니다. 규칙은 첫 번째 문자의 az, AZ, _ 및 다음 문자의 + 0-9 중 하나입니다.

밑줄 접두사는 C 코드에서 일반적입니다. 단일 밑줄은 "개인"을 의미하며 이중 밑줄은 일반적으로 컴파일러에서 사용하도록 예약되어 있습니다.


3
그것들은 도서관에서 일반적입니다. 사용자 코드에서 공통적이지 않아야합니다.
Martin York

43
사람들 C로 라이브러리를 작성합니다.
John Millikin

7
"예, 밑줄은 식별자의 어느 곳에 나 사용될 수 있습니다." 이것은 전역 식별자에 잘못되었습니다. Roger의 답변을 참조하십시오 .
sbi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.