문자열 리터럴

문자열 리터럴 (String literal)

소스 코드에 문자열을 적을 때 쓰는 것이 문자열 리터럴이에요. C++에는 일반 문자열뿐 아니라 와이드, UTF-8/16/32, 그리고 원시(raw) 문자열 리터럴이 있어요. 이 페이지에서는 문자열 리터럴의 문법과 종류, 초기화·연결 규칙을 정리할게요.

출처: cppreference

본문

문법

"s-char-seq(선택)" (1)
R"d-char-seq(선택)(r-char-seq(선택))d-char-seq(선택)" (2) (C++11부터)
L"s-char-seq(선택)" (3)
LR"d-char-seq(선택)(r-char-seq(선택))d-char-seq(선택)" (4) (C++11부터)
u8"s-char-seq(선택)" (5) (C++11부터)
u8R"d-char-seq(선택)(r-char-seq(선택))d-char-seq(선택)" (6) (C++11부터)
u"s-char-seq(선택)" (7) (C++11부터)
uR"d-char-seq(선택)(r-char-seq(선택))d-char-seq(선택)" (8) (C++11부터)
U"s-char-seq(선택)" (9) (C++11부터)
UR"d-char-seq(선택)(r-char-seq(선택))d-char-seq(선택)" (10) (C++11부터)

동작 방식 (Explanation)

항목 의미
s-char-seq - 하나 이상의 s-char들의 시퀀스
s-char - 다음 중 하나 — basic-s-char — 이스케이프 시퀀스에 정의된 이스케이프 시퀀스 — 이스케이프 시퀀스에 정의된 유니버설 문자 이름
basic-s-char - 번역 문자 집합의 문자. 단 쌍따옴표 ", 백슬래시 \, 개행 문자는 제외
d-char-seq - 하나 이상의 d-char들의 시퀀스 (최대 16자)
d-char - 기본 문자 집합의 문자. 단 괄호, 백슬래시, 공백은 제외
r-char-seq - 하나 이상의 r-char들의 시퀀스. 단 종결 시퀀스 )d-char-seq"를 포함하면 안 됨
r-char - 번역 문자 집합의 문자
문법 종류 타입 인코딩
(1,2) 일반 문자열 리터럴 const char[N] 일반 리터럴 인코딩
(3,4) 와이드 문자열 리터럴 const wchar_t[N] 와이드 리터럴 인코딩
(5,6) UTF-8 문자열 리터럴 const char[N](C++20 이전) / const char8_t[N](C++20부터) UTF-8
(7,8) UTF-16 문자열 리터럴 const char16_t[N] UTF-16
(9,10) UTF-32 문자열 리터럴 const char32_t[N] UTF-32

위 표의 타입에서 N은 인코딩된 코드 유닛의 수로, 아래에서 결정돼요.

일반 및 UTF-8(C++11부터) 문자열 리터럴을 통틀어 좁은 문자열 리터럴(narrow string literals)이라고 해요.

문자열 리터럴을 평가하면 static 저장 기간을 가진 문자열 리터럴 객체가 생성돼요. 모든 문자열 리터럴이 겹치지 않는 객체에 저장되는지, 그리고 문자열 리터럴을 연속 평가할 때 같은 객체가 나오는지 다른 객체가 나오는지는 미지정이에요.

문자열 리터럴 객체를 수정하려는 시도의 효과는 정의되지 않아요(UB).

bool b = "bar" == 3 + "foobar"; // true일 수도 false일 수도, 미지정

const char* pc = "Hello";
char* p = const_cast<char*>(pc);
p[0] = 'M'; // 정의되지 않은 동작(UB)
### 원시 문자열 리터럴 원시(raw) 문자열 리터럴은 R을 포함하는 접두사를 가진 문자열 리터럴이에요(문법 (2,4,6,8,10)). 어떤 문자도 이스케이프하지 않아요. 즉 구분자 d-char-seq()d-char-seq 사이의 모든 것이 문자열의 일부가 돼요. 종결 d-char-seq는 시작 d-char-seq와 같은 문자 시퀀스여야 해요. cpp // OK: 백슬래시 하나 포함. // "\\"와 동등 R"(\ )"; // OK: \n 네 쌍 포함. // "\n\n\n\n"와 동등 R"(\n\n\n\n)"; // OK: 닫는 괄호 하나, 쌍따옴표 두 개, 여는 괄호 하나 포함. // ")\"\"("와 동등 R"-( )""( )-"; // OK: "\n)\\\na\"\"\n"와 동등 R"a( )\ a"" )a"; // OK: "x = \"\"\\y\"\""와 동등 R"( x = ""\y"" )"; // R"<<(-_-)>>"; // 오류: 시작·끝 구분자가 일치하지 않음 // R"-()-\"-()-"; // 오류: )-"가 중간에 나타나 리터럴을 종료함 (C++11부터)

초기화 (Initialization)

문자열 리터럴 객체는 문자열 리터럴의 s-char(r-char, C++11부터) 시퀀스에 대응하는 코드 유닛 값들의 시퀀스에, 종결 널 문자(U+0000) 하나를 더한 것으로 초기화돼요. 순서는 다음과 같아요:

  1. 인접한 basic-s-char·r-char(C++11부터) 시퀀스, 단순 이스케이프 시퀀스, 유니버설 문자 이름 각각에 대해, 그 문자가 나타내는 문자 시퀀스가 문자열 리터럴의 연관 문자 인코딩을 사용해 코드 유닛 시퀀스로 인코딩돼요. 어떤 문자가 연관 문자 인코딩에 표현이 없으면 프로그램은 ill-formed예요.

연관 문자 인코딩이 stateful이면, 첫 번째 시퀀스는 초기 인코딩 상태로 시작해 인코딩되고, 이후 각 시퀀스는 이전 시퀀스의 최종 인코딩 상태로 시작해 인코딩돼요.

  1. 숫자 이스케이프 시퀀스에 대해, 이스케이프 시퀀스의 숫자들이 이루는 8진수(또는 16진수) 수가 나타내는 정수 값을 v, 문자열 리터럴의 배열 요소 타입(위 표 참고)을 T라고 할 때:
  • vT의 표현 가능 값 범위를 넘지 않으면, 이스케이프 시퀀스는 값 v의 단일 코드 유닛 하나를 기여해요.
  • 그렇지 않고, 문자열 리터럴이 문법 (1) 또는 (3)이고, vT의 기본 타입에 해당하는 부호 없는 타입의 표현 가능 범위를 넘지 않는 경우(C++11부터), 이스케이프 시퀀스는 v mod 2^S에 합동인 T 타입의 고유한 값의 단일 코드 유닛을 기여해요. 여기서 ST의 너비예요.
  • 그 외에는 프로그램은 ill-formed예요.

연관 문자 인코딩이 stateful이면, 이 모든 시퀀스는 인코딩 상태에 영향을 주지 않아요.

연관 문자 인코딩이 stateful이면, 이 시퀀스들이 인코딩 상태에 미치는 효과는 구현 정의예요.

연결 (Concatenation)

인접한 문자열 리터럴은 번역 단계 6에서(전처리 후) 연결돼요:

  • 두 문자열 리터럴이 같은 종류면, 연결된 문자열 리터럴도 같은 종류예요.
  • 일반 문자열 리터럴이 와이드 문자열 리터럴에 인접하면 동작은 정의되지 않아요(C++11 이전).
  • 일반 문자열 리터럴이 비-일반 문자열 리터럴에 인접하면, 연결된 리터럴은 후자의 종류예요. UTF-8 문자열 리터럴이 와이드 문자열 리터럴에 인접하면 프로그램은 ill-formed예요. 그 외 다른 조합은 구현 정의 의미로 조건부 지원돼요.[1] (C++11부터, C++23 이전)
  • 그 외 다른 조합은 ill-formed예요(C++23부터).
"Hello, " "world!"  // 단계 6에서, 두 문자열 리터럴이 "Hello, world!"를 이룸

L"Δx = %" PRId16   // 단계 4에서 PRId16이 "d"로 확장됨
                   // 단계 6에서 L"Δx = %"와 "d"가 L"Δx = %d"를 이룸
  1. ↑이런 연결을 지원하는 알려진 구현은 없다.

한정되지 않은 문자열 (Unevaluated strings)

다음 맥락은 문자열 리터럴을 기대하지만 평가하지 않아요:

비-일반 문자열 리터럴이 이런 맥락에서 허용되는지는 미지정이고, 단 리터럴 연산자 이름은 일반 문자열 리터럴을 사용해야 해요(C++11부터). (C++26 이전)
이런 맥락에서는 일반 문자열 리터럴만 허용돼요. 한정되지 않은 문자열 안의 각 유니버설 문자 이름과 각 단순 이스케이프 시퀀스는, 그것이 나타내는 번역 문자 집합의 멤버로 대체돼요. 숫자 이스케이프 시퀀스나 조건부 이스케이프 시퀀스를 포함한 한정되지 않은 문자열은 ill-formed예요. (C++26부터)

주의할 점 (Notes)

문자열 리터럴은 문자 배열 초기화에 쓸 수 있어요. 배열이 char str[]="foo";처럼 초기화되면 str은 문자열 "foo"의 복사본을 담아요.

문자열 리터럴은 C와의 호환을 위해 비-const char*wchar_t*로 변환·대입 가능했어요(C에서 문자열 리터럴은 char[N]·wchar_t[N] 타입). 이런 암묵적 변환은 폐기(deprecated)됐어요. (C++11 이전)
문자열 리터럴은 비-const CharT*로 변환·대입되지 않아요. 그런 변환이 필요하면 명시적 캐스트(예: const_cast)를 써야 해요. (C++11부터)

문자열 리터럴이 반드시 널-종결 문자 시퀀스인 것은 아니에요. 문자열 리터럴에 내장 널 문자가 있으면, 둘 이상의 문자열을 담은 배열을 나타내요:

const char* p = "abc\0def"; // std::strlen(p) == 3, 하지만 배열 크기는 8

문자열 리터럴에서 16진수 이스케이프 시퀀스 뒤에 유효한 16진수 숫자가 오면, 잘못된 이스케이프 시퀀스라서 컴파일이 실패해요. 문자열 연결을 우회책으로 쓸 수 있어요:

//const char* p = "\xfff"; // 오류: 16진수 이스케이프 시퀀스가 범위를 벗어남
const char* p = "\xff""f"; // OK: const char[3]로 {'\xff','f','\0'} 보유

예제

다양한 문자열 리터럴의 종류가 실제로 어떤 값을 갖는지 확인해 볼게요.

#include <iostream>

// array1과 array2는 같은 값을 담음:
char array1[] = "Foo" "bar";
char array2[] = { 'F', 'o', 'o', 'b', 'a', 'r', '\0' };

const char* s1 = R"foo(Hello World)foo"; // "Hello World"와 같음
const char* s2 = "\nHello\nWorld\n";     // 같은 값
const char* s3 = "\n" "Hello\n" " World\n";

const wchar_t* s4 = L"ABC" L"DEF"; // OK, L"ABCDEF"와 같음
const wchar_t* s5 = L"ABCDEF";

const char32_t* s6 = U"GHI" "JKL"; // OK, U"GHIJKL"과 같음
const char32_t* s7 = U"GHIJKL";

const char16_t* s9 = "MN" u"OP" "QR"; // OK, u"MNOPQR"과 같음
const char16_t* sA = u"MNOPQR";

// const auto* sB = u"Mixed" U"Types"; // C++23 이전엔 구현에 따라 지원될 수도, ill-formed
const wchar_t* sC = LR"---(STUV)---"; // OK, 원시 문자열 리터럴

int main()
{
    std::cout << array1 << ' ' << array2 << '\n'
              << s1 << s2 << s3 << std::endl;
    std::wcout << s4 << ' ' << s5 << ' ' << sC << std::endl;
}

출력:

Foobar Foobar
Hello World
Hello World
Hello World
ABCDEF ABCDEF STUV

"Foo" "bar"처럼 인접한 리터럴이 연결돼 Foobar가 돼요. s1은 원시 문자열이라 R"foo(...)foo" 사이의 텍스트가 이스케이프 없이 그대로 와요. 서로 다른 접두사(u/U/L)의 리터럴도 인접하면 연결 규칙에 따라 합쳐져요.

결함 보고 (Defect reports)

다음 동작 변경 결함 보고는 과거에 발표된 C++ 표준에 소급 적용됐어요.

DR 적용 표준 발표 당시 동작 수정된 동작
CWG 411 (P2029R4) C++98 문자열 리터럴의 이스케이프 시퀀스가 여러 코드 유닛에 매핑될 수 없었음 허용됨
CWG 1656 (P2029R4) C++98 문자열 리터럴의 숫자 이스케이프 시퀀스가 가리키는 문자가 불명확했음 명확해짐
CWG 1759 C++11 UTF-8 문자열 리터럴이 char로 표현할 수 없는 코드 유닛을 가질 수 있었음 char는 모든 UTF-8 코드 유닛을 표현 가능
CWG 1823 C++98 문자열 리터럴이 서로 구별되는지가 구현 정의였음 구별성은 미지정, 같은 리터럴이 다른 객체를 만들 수 있음
CWG 2333 (P2029R4) C++11 UTF-8/16/32 문자열 리터럴에 숫자 이스케이프 시퀀스가 허용되는지 불명확했음 명확해짐
CWG 2870 C++11 두 일반 문자열 리터럴의 연결 결과가 불명확했음 명확해짐
P1854R4 C++98 인코딩 불가한 문자의 일반·와이드 문자열 리터럴이 조건부 지원이었음 그런 리터럴을 가진 프로그램은 ill-formed
P2029R4 C++98 1. 문자열 리터럴이 인코딩 불가한 문자를 담을 수 있는지 불명확 2. 코드 유닛이 리터럴 배열 요소 타입으로 표현 불가능한 숫자 이스케이프 시퀀스를 담을 수 있는지 불명확 1. 일반·와이드 리터럴에서 조건부 지원으로[1] 2. 코드 유닛이 기본 타입에 해당하는 부호 없는 정수 타입으로도 표현 불가능하면 ill-formed
  1. ↑P1854R4는 나중에 DR로 채택되어 이 해결책을 덮어썼다.

더 알아보기