Namespaces
Variants

Phases of translation

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

C++ 소스 파일은 컴파일러에 의해 처리되어 C++ 프로그램을 생성합니다.

목차

번역 과정

C++ 프로그램의 텍스트는 소스 파일 이라는 단위로 유지됩니다.

C++ 소스 파일은 다음 단계를 거쳐 번역(translation) 되어 번역 단위(translation unit) 가 됩니다:

  1. 각 소스 파일을 문자 시퀀스에 매핑합니다.
  2. 각 문자 시퀀스를 공백으로 구분된 전처리 토큰 시퀀스로 변환합니다.
  3. 각 전처리 토큰을 토큰으로 변환하여 토큰 시퀀스를 형성합니다.
  4. 각 토큰 시퀀스를 번역 단위로 변환합니다.

C++ 프로그램은 번역된 번역 단위들로 구성될 수 있습니다. 번역된 번역 단위들과 인스턴스화된 단위들(인스턴스화된 단위들은 아래 8단계에서 설명됨)은 개별적으로 저장되거나 라이브러리에 저장될 수 있습니다. 여러 번역 단위들은 (예를 들어) 외부 링크를 가진 심볼들이나 데이터 파일들을 통해 서로 통신합니다. 번역 단위들은 별도로 번역된 후 나중에 링크되어 실행 가능한 프로그램을 생성할 수 있습니다.

위의 과정은 9개의 translation phases 로 구성될 수 있습니다.

전처리 토큰

전처리 토큰 은 번역 단계 3부터 6까지 언어의 최소 어휘 요소입니다.

전처리 토큰의 범주는 다음과 같습니다:

(C++20부터)
프로그램은 이 범주에 해당하는 문자가 다음 중 하나일 경우 형식이 잘못되었습니다:
  • 아포스트로피 ( ' , U+0027),
  • 따옴표 ( " , U+0022), 또는
  • 기본 문자 집합 에 포함되지 않은 문자인 경우.

전처리 숫자

전처리 숫자의 전처리 토큰 집합은 정수 리터럴 부동소수점 리터럴 의 토큰 집합 합집합의 상위 집합입니다:

. (선택 사항) digit pp-continue-seq  (선택 사항)
digit - 0-9 숫자 중 하나
pp-continue-seq - pp-continue 들의 연속

pp-continue 는 다음 중 하나입니다:

identifier-continue (1)
exp-char sign-char (2)
. (3)
digit (4) (C++14 이후)
nondigit (5) (C++14 이후)
identifier-continue - 유효한 식별자 의 첫 번째가 아닌 모든 문자
exp-char - 다음 중 하나 P , p , (C++11부터) E e
sign-char - 다음 중 하나 + -
digit - 숫자 0-9 중 하나
nondigit - 라틴 문자 A/a-Z/z 및 밑줄 중 하나

전처리 숫자는 타입이나 값을 가지지 않으며, 정수/부동소수점 리터럴 토큰으로의 성공적인 변환 후에 둘 다 획득합니다.

공백

공백 주석 , 공백 문자, 또는 둘 다로 구성됩니다.

다음 문자들은 공백 문자입니다:

  • 문자 탭 (U+0009)
  • 줄 바꿈 / 새 줄 문자 (U+000A)
  • 줄 탭 (U+000B)
  • 폼 피드 (U+000C)
  • 공백 (U+0020)

공백은 일반적으로 전처리 토큰을 분리하는 데 사용되며, 다음과 같은 예외가 있습니다:

  • 헤더 이름, 문자 리터럴 및 문자열 리터럴 내에서는 구분자가 아닙니다.
  • 개행 문자를 포함하는 공백으로 구분된 전처리 토큰은 preprocessing directives 를 형성할 수 없습니다.
#include "my header"        // OK, 공백을 포함하는 헤더 이름 사용
#include/*hello*/<iostream> // OK, 주석을 공백으로 사용
#include
<iostream> // Error: #include는 여러 줄에 걸쳐 있을 수 없음
"str ing"  // OK, 단일 전처리 토큰 (문자열 리터럴)
' '        // OK, 단일 전처리 토큰 (문자 리터럴)

최대 먼치

최대 먼치(maximal munch)는 3단계에서 소스 파일을 전처리 토큰으로 분해할 때 사용되는 규칙입니다.

입력이 주어진 문자까지 전처리 토큰으로 파싱된 경우(그렇지 않으면 다음 전처리 토큰이 파싱되지 않아 파싱 순서가 고유해짐), 다음 전처리 토큰은 일반적으로 후속 분석이 실패하더라도 전처리 토큰을 구성할 수 있는 가장 긴 문자 시퀀스로 간주됩니다. 이는 일반적으로 최대한 많이 집어삼키기(maximal munch) 로 알려져 있습니다.

int foo = 1;
int bar = 0xE+foo;   // 오류: 유효하지 않은 전처리 숫자 0xE+foo
int baz = 0xE + foo; // 정상

다시 말해, 최대한 많이 씹기 규칙은 multi-character operators and punctuators 를 우선시합니다:

int foo = 1;
int bar = 2;
int num1 = foo+++++bar; // 오류: "foo++ ++ +baz"로 처리됨, "foo++ + ++baz"가 아님
int num2 = -----foo;    // 오류: "-- -- -foo"로 처리됨, "- -- --foo"가 아님

최대 먼치 규칙에는 다음과 같은 예외가 있습니다:

  • 헤더 이름 전처리 토큰은 다음 경우에만 형성됩니다:
  • #include 지시문에서 include 전처리 토큰 뒤에 나오는
(C++17부터)
  • import 지시문에서 import 전처리 토큰 뒤에
(C++20부터)
std::vector<int> x; // OK, "int"는 헤더 이름이 아님
  • 다음 세 문자가 < :: 이고, 이후 문자가 : 도 아니고 > 도 아닌 경우, < alternative token < : 의 첫 번째 문자로 처리되지 않고 단독 전처리 토큰으로 처리됩니다.
struct Foo { static const int v = 1; };
std::vector<::Foo> x;  // OK, <:가 [에 대한 대체 토큰으로 해석되지 않음
extern int y<::>;      // OK, "extern int y[];"와 동일
int z<:::Foo::value:>; // OK, "int z[::Foo::value];"와 동일
  • 다음 두 문자가 >> 이고, 두 > 문자 중 하나가 템플릿 식별자 를 완성할 수 있는 경우, 해당 문자는 전처리 토큰 >> 의 일부가 아닌 단독 전처리 토큰으로 처리됩니다.
template<int i> class X { /* ... */ };
template<class T> class Y { /* ... */ };
Y<X<1>> x3;      // OK, "Y<X<1> >" 타입의 변수 "x3"을 선언
Y<X<6>>1>> x4;   // 구문 오류
Y<X<(6>>1)>> x5; // OK
  • 다음 문자가 원시 문자열 리터럴 의 접두사와 시작 큰따옴표로 시작하는 문자 시퀀스인 경우, 다음 전처리 토큰은 원시 문자열 리터럴입니다. 리터럴은 원시 문자열 패턴과 일치하는 가장 짧은 문자 시퀀스로 구성됩니다.
#define R "x"
const char* s = R"y";         // 잘못된 원시 문자열 리터럴, "x" "y"가 아님
const char* s2 = R"(a)" "b)"; // 원시 문자열 리터럴 뒤에 일반 문자열 리터럴이 옴
(C++11부터)

토큰

토큰은 번역 단계 7에서 언어의 최소 어휘 요소입니다.

토큰의 범주는 다음과 같습니다:

번역 단계

번역은 1단계부터 9단계까지의 순서로 as if 수행됩니다. 실제로는 서로 다른 단계들이 함께 결합될 수 있지만, 구현체들은 이러한 개별 단계들이 발생하는 것처럼 동작합니다.

Phase 1: 소스 문자 매핑

1) 소스 코드 파일의 개별 바이트들은 (구현 정의 방식으로) 기본 소스 문자 집합 의 문자들에 매핑됩니다. 특히, 운영체제에 종속적인 줄 끝 표시자는 개행 문자로 대체됩니다.
2) 허용되는 소스 파일 문자 집합은 구현 정의됩니다 (C++11부터) . 기본 소스 문자 집합 의 문자로 매핑될 수 없는 소스 파일 문자는 해당 문자의 유니버설 문자 이름 ( \u 또는 \U 로 이스케이프됨)이나 동등하게 처리되는 구현 정의 형태로 대체됩니다.
3) 트라이그래프 시퀀스 는 해당하는 단일 문자 표현으로 대체됩니다.
(C++17까지)
(C++23까지)

UTF-8 코드 단위 시퀀스인 입력 파일(UTF-8 파일)은 지원이 보장됩니다. 지원되는 다른 종류의 입력 파일 집합은 구현 정의됩니다. 집합이 비어있지 않은 경우, 입력 파일의 종류는 입력 파일을 그 내용과 무관하게 UTF-8 파일로 지정하는 수단을 포함하는 구현 정의 방식으로 결정됩니다(바이트 순서 표시를 인식하는 것만으로는 충분하지 않음).

  • 입력 파일이 UTF-8 파일로 결정되면, 해당 파일은 올바른 형식의 UTF-8 코드 단위 시퀀스여야 하며, 유니코드 스칼라 값 시퀀스를 생성하기 위해 디코딩됩니다. 그런 다음 각 유니코드 스칼라 값을 해당하는 변환 문자 집합 요소에 매핑하여 변환 문자 집합 요소 시퀀스가 형성됩니다. 결과 시퀀스에서 캐리지 리턴(U+000D) 다음에 라인 피드(U+000A)가 오는 입력 시퀀스의 각 문자 쌍과, 캐리지 리턴(U+000D) 바로 다음에 라인 피드(U+000A)가 오지 않는 각 캐리지 리턴은 단일 개행 문자로 대체됩니다.
  • 구현에서 지원하는 다른 종류의 입력 파일에 대해서는, 문자들이 (구현 정의 방식으로) 변환 문자 집합 요소 시퀀스에 매핑됩니다. 특히, 운영체제에 종속적인 줄 끝 표시자는 개행 문자로 대체됩니다.
(C++23부터)

Phase 2: 라인 스플라이싱

1) 첫 번째 변환 문자가 바이트 순서 표시(U+FEFF)인 경우 삭제됩니다. (since C++23) 백슬래시( \ )가 줄 끝에 나타날 때마다(바로 뒤에 개행 문자를 제외한 0개 이상의 공백 문자가 오고 (since C++23) 개행 문자가 오는 경우), 이러한 문자들은 삭제되어 두 개의 물리적 소스 줄을 하나의 논리적 소스 줄로 결합합니다. 이는 단일 패스 연산입니다; 두 개의 백슬래시로 끝나고 빈 줄이 뒤따르는 줄은 세 줄을 하나로 결합하지 않습니다.
2) 비어 있지 않은 소스 파일이 이 단계 이후에 개행 문자로 끝나지 않는 경우(이 시점에서 줄 끝의 백슬래시는 더 이상 접합이 아님), 종료 개행 문자가 추가됩니다.

Phase 3: 렉싱

1) 소스 파일은 전처리 토큰 공백 문자 로 분해됩니다:
// The following #include directive can de decomposed into 5 preprocessing tokens:
//     punctuators (#, < and >)
//          │
// ┌────────┼────────┐
// │        │        │
   #include <iostream>
//     │        │
//     │        └── header name (iostream)
//     │
//     └─────────── identifier (include)
소스 파일이 부분적인 전처리 토큰이나 부분적인 주석으로 끝나는 경우, 프로그램은 ill-formed입니다:
// Error: partial string literal
"abc
// Error: partial comment
/* comment
소스 파일의 문자들이 다음 전처리 토큰을 형성하기 위해 소비될 때(즉, 주석이나 다른 형태의 공백의 일부로 소비되지 않을 때), 유니버설 문자 이름이 인식되고 변환 문자 집합 의 지정된 요소로 대체됩니다. 단, 다음 전처리 토큰들 중 하나에서 문자 시퀀스를 매칭하는 경우는 예외입니다:
  • 문자 리터럴 ( c-char-sequence )
  • 문자열 리터럴 ( s-char-sequence r-char-sequence ), 구분자 제외 ( d-char-sequence )
  • 헤더 이름 ( h-char-sequence q-char-sequence )
(C++23부터)


2) 단계 1 및 단계 2에서 수행된 모든 변환은 (C++23까지) 모든 raw string literal 의 처음과 마지막 큰따옴표 사이에서 원래 상태로 복원됩니다.
(C++11부터)
3) 공백 문자는 변환됩니다:
  • 각 주석은 하나의 공백 문자로 대체됩니다.
  • 줄바꿈 문자는 유지됩니다.
  • 줄바꿈 이외의 공백 문자로 이루어진 각 비어 있지 않은 시퀀스가 유지되거나 하나의 공백 문자로 대체되는지는 명시되지 않습니다.

Phase 4: 전처리

1) 전처리기 가 실행됩니다.
2) #include 지시문으로 도입된 각 파일은 1단계부터 4단계까지 재귀적으로 진행됩니다.
3) 이 단계가 끝나면 모든 전처리기 지시문이 소스에서 제거됩니다.

Phase 5: 공통 문자열 리터럴 인코딩 결정

1) 문자 리터럴 문자열 리터럴 의 모든 문자는 소스 문자 집합에서 인코딩 으로 변환됩니다 ( 기본 문자 집합 의 96개 문자가 단일 바이트 표현을 가지는 경우 UTF-8과 같은 멀티바이트 문자 인코딩일 수 있습니다).
2) 이스케이프 시퀀스 와 유니버설 문자 이름은 문자 리터럴과 비-원시 문자열 리터럴에서 확장되어 리터럴 인코딩으로 변환됩니다.

유니버설 문자 이름으로 지정된 문자가 해당 리터럴 인코딩에서 단일 코드 포인트로 인코딩될 수 없는 경우, 결과는 구현에 따라 정의되지만 널 (와이드) 문자가 아니라는 것이 보장됩니다.

(C++23까지)

두 개 이상의 인접한 문자열 리터럴 토큰 시퀀스에 대해, 공통 인코딩 접두사는 여기 에 설명된 대로 결정됩니다. 각 해당 문자열 리터럴 토큰은 그 공통 인코딩 접두사를 가지는 것으로 간주됩니다. (문자 변환은 3단계로 이동됨)

(C++23부터)

단계 6: 문자열 리터럴 연결하기

인접한 string literals 은 연결됩니다.

단계 7: 컴파일

컴파일이 수행됩니다: 각 전처리 토큰은 token 으로 변환됩니다. 토큰들은 구문 및 의미적으로 분석되고 translation unit 으로 번역됩니다.

단계 8: 템플릿 인스턴스화

각 번역 단위는 명시적 인스턴스화 로 요청된 것들을 포함하여 필요한 템플릿 인스턴스화 목록을 생성하기 위해 검사됩니다. 템플릿의 정의들이 위치를 찾아내고, 필요한 인스턴스화들이 수행되어 인스턴스화 단위 를 생성합니다.

단계 9: 링킹

외부 참조를 충족시키기 위해 필요한 번역 단위, 인스턴스화 단위, 그리고 라이브러리 구성 요소들은 실행 환경에서 실행에 필요한 정보를 포함하는 프로그램 이미지로 수집됩니다.

참고 사항

소스 파일, 번역 단위 및 번역된 번역 단위는 반드시 파일로 저장될 필요가 없으며, 이러한 개체들과 외부 표현 간에 일대일 대응이 반드시 존재할 필요도 없습니다. 이 설명은 개념적일 뿐이며, 특정 구현을 명시하지 않습니다.

5단계에서 수행되는 변환은 일부 구현에서 명령줄 옵션으로 제어할 수 있습니다: gcc와 clang은 - finput - charset 을 사용하여 소스 문자 집합의 인코딩을 지정하고, - fexec - charset - fwide - exec - charset 을 사용하여 각각 일반 리터럴 인코딩과 와이드 리터럴 인코딩을 지정합니다. 반면 Visual Studio 2015 업데이트 2 및 이후 버전은 / source - charset / execution - charset 을 사용하여 각각 소스 문자 집합과 리터럴 인코딩을 지정합니다.

(C++23까지)

일부 컴파일러는 인스턴스화 유닛(일명 template repositories 또는 template registries )을 구현하지 않고 단순히 7단계에서 각 템플릿 인스턴스화를 컴파일하여, 암시적 또는 명시적으로 요청된 객체 파일에 코드를 저장한 다음, 링커가 9단계에서 이러한 컴파일된 인스턴스화들을 하나로 통합합니다.

결함 보고서

다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 787 C++98 비어 있지 않은 소스 파일이 2단계 끝에서
개행 문자로 끝나지 않을 경우 동작이 정의되지 않음
이 경우 종료 개행 문자를
추가함
CWG 1104 C++98 대체 토큰 < : 때문에 std:: vector < :: std:: string >
std:: vector [ : std:: string > 로 처리됨
이 경우를 처리하기 위한 추가적인
렉싱 규칙을 추가함
CWG 1775 C++11 2단계에서 원시 문자열 리터럴 내부에서
유니버설 문자 이름을 형성하면 정의되지 않은 동작이 발생함
올바르게 정의됨
CWG 2747 C++98 2단계에서 스플라이싱 후 파일 끝 스플라이스를 확인했으나 이는 불필요함 확인을 제거함
P2621R3 C++98 유니버설 문자 이름을 라인 스플라이싱이나
토큰 연결로 형성하는 것이 허용되지 않았음
허용됨

참고문헌

  • C++23 표준 (ISO/IEC 14882:2024):
  • 5.2 번역 단계 [lex.phases]
  • C++20 표준(ISO/IEC 14882:2020):
  • 5.2 변환 단계 [lex.phases]
  • C++17 표준 (ISO/IEC 14882:2017):
  • 5.2 변환 단계 [lex.phases]
  • C++14 표준 (ISO/IEC 14882:2014):
  • 2.2 번역 단계 [lex.phases]
  • C++11 표준 (ISO/IEC 14882:2011):
  • 2.2 번역 단계 [lex.phases]
  • C++03 표준 (ISO/IEC 14882:2003):
  • 2.1 번역 단계 [lex.phases]
  • C++98 표준 (ISO/IEC 14882:1998):
  • 2.1 번역 단계 [lex.phases]

참고 항목

C 문서 참조: 번역 단계