Namespaces
Variants

String literals

From cppreference.net

소스 코드에 문자열을 내장해야 할 때 사용되는, 지정된 문자 배열 타입의 이름 없는 객체를 제자리에 생성합니다.

목차

구문

" s-char-sequence " (1)
u8" s-char-sequence " (2) (C11부터)
u" s-char-sequence " (3) (C11부터)
U" s-char-sequence " (4) (C11부터)
L" s-char-sequence " (5)

여기서

s-char-sequence - 소스 문자 집합에서 가져온 0개 이상의 문자들(( " ), \ , 및 개행 문자 제외) 또는 이스케이프 시퀀스 에 정의된 문자 이스케이프, 16진수 이스케이프, 8진수 이스케이프 , 또는 유니버설 문자 이름 (C99부터) 중 하나인 문자들로 구성됩니다.
1) character string literal : 리터럴의 타입은 char [ N ] 입니다. 여기서 N 은 실행 narrow 인코딩의 코드 단위로 측정한 문자열의 크기로, null 종결자를 포함합니다. 배열의 각 char 요소는 실행 문자 집합을 사용하여 s-char-sequence 의 다음 문자로부터 초기화됩니다.
2) UTF-8 문자열 리터럴 : 리터럴의 타입은 char [ N ] (C23 이전) char8_t [ N ] (C23 이후) 입니다. 여기서 N 은 널 종결자를 포함한 UTF-8 코드 단위의 문자열 크기입니다. 배열 내 각 char (C23 이전) char8_t (C23 이후) 요소는 UTF-8 인코딩을 사용하여 s-char-sequence 의 다음 멀티바이트 문자로부터 초기화됩니다.
3) 16비트 너비 문자열 리터럴: 리터럴의 타입은 char16_t [ N ] 이며, 여기서 N 은 구현에서 정의된 16비트 인코딩(일반적으로 UTF-16)의 코드 단위로 측정한 문자열 크기로, 널 종결자를 포함합니다. 배열 내 각 char16_t 요소는 구현에서 정의된 로캘에서 mbrtoc16 을 실행하는 것처럼 초기화됩니다.
4) 32비트 너비 문자열 리터럴: 리터럴의 타입은 char32_t [ N ] 이며, 여기서 N 은 구현에서 정의된 32비트 인코딩(일반적으로 UTF-32)의 코드 단위로 측정한 문자열 크기로, 널 종결자를 포함합니다. 배열 내 각 char32_t 요소는 구현에서 정의된 로캘에서 mbrtoc32 을 실행하는 것처럼 초기화됩니다.
(C23 이전)
3) UTF-16 문자열 리터럴 : 리터럴의 타입은 char16_t [ N ] 이며, 여기서 N 은 UTF-16 코드 단위로 측정한 문자열 크기로, 널 종결자를 포함합니다. 배열 내 각 char16_t 요소는 s-char-sequence 의 다음 멀티바이트 문자를 UTF-16 인코딩을 사용하여 초기화됩니다.
4) UTF-32 문자열 리터럴 : 리터럴의 타입은 char32_t [ N ] 이며, 여기서 N 은 UTF-32 코드 단위로 측정한 문자열 크기로, 널 종결자를 포함합니다. 배열 내 각 char32_t 요소는 s-char-sequence 의 다음 멀티바이트 문자를 UTF-32 인코딩을 사용하여 초기화됩니다.
(C23 이후)
5) 와이드 문자열 리터럴: 리터럴의 타입은 wchar_t [ N ] 입니다. 여기서 N 은 실행 와이드 인코딩의 코드 단위로 측정한 문자열의 크기로, 널 종결자를 포함합니다. 배열 내 각 wchar_t 요소는 구현 정의 로캘에서 mbstowcs 를 실행하는 것처럼 초기화됩니다.

설명

먼저, 번역 단계 6 (매크로 확장 이후)에서 인접한 문자열 리터럴(즉, 공백으로만 구분된 문자열 리터럴)이 연결됩니다.

두 개의 좁은 문자열 리터럴 또는 두 개의 넓은 문자열 리터럴만 연결할 수 있습니다.

(until C99)

접두사가 없는 리터럴이 하나 있는 경우, 결과 문자열 리터럴은 접두사가 있는 리터럴에 지정된 너비/인코딩을 가집니다.

L"Δx = %" PRId16 // at phase 4, PRId16 expands to "d"
                 // at phase 6, L"Δx = %" and "d" form L"Δx = %d"
(since C99)

두 문자열 리터럴이 서로 다른 인코딩 접두사를 가지고 있는 경우, 연결은 구현에 따라 정의됩니다. 단, UTF-8 문자열 리터럴과 와이드 문자열 리터럴은 연결할 수 없습니다.

(since C11)
(until C23)

두 문자열 리터럴이 서로 다른 인코딩 접두사를 가지고 있는 경우, 연결은 형식 오류입니다.

(since C23)

둘째, 번역 단계 7 에서 각 문자열 리터럴에 종료 널 문자가 추가된 다음, 각 리터럴은 문자열 리터럴의 내용과 널 종결자를 위한 공간 하나를 포함할 만큼의 길이를 가진 정적 저장 기간 을 가진 이름 없는 배열을 초기화합니다.

char* p = "\x12" "3"; // char[3] 크기의 정적 배열을 생성하여 {'\x12', '3', '\0'}를 저장
                      // p를 배열의 첫 번째 요소를 가리키도록 설정

문자열 리터럴은 수정할 수 없습니다 (실제로 .rodata 와 같은 읽기 전용 메모리에 배치될 수 있습니다). 프로그램이 문자열 리터럴로 형성된 정적 배열을 수정하려고 시도하면 동작은 정의되지 않습니다.

char* p = "Hello";
p[1] = 'M'; // 정의되지 않은 동작
char a[] = "Hello";
a[1] = 'M'; // 정상: a는 문자열 리터럴이 아님

동일한 문자열 리터럴이 메모리 내 동일한 위치를 참조하는 것은 필수 사항도 아니고 금지된 사항도 아닙니다. 더 나아가, 겹치는 문자열 리터럴이나 다른 문자열 리터럴의 부분 문자열인 문자열 리터럴들은 결합될 수 있습니다.

"def" == 3+"abcdef"; // 1 또는 0일 수 있으며, 구현에 따라 정의됨

참고 사항

문자열 리터럴이 반드시 문자열인 것은 아닙니다; 문자열 리터럴에 내장된 null 문자가 있는 경우, 하나 이상의 문자열을 포함하는 배열을 나타냅니다:

char* p = "abc\0def"; // strlen(p) == 3, 하지만 배열의 크기는 8입니다

문자열 리터럴에서 16진수 이스케이프 뒤에 유효한 16진수 숫자가 오면 잘못된 이스케이프 시퀀스로 컴파일이 실패하지만, 문자열 연결을 우회 방법으로 사용할 수 있습니다:

//char* p = "\xfff"; // 오류: 16진수 이스케이프 시퀀스가 범위를 벗어남
char* p = "\xff""f"; // 정상, 리터럴은 {'\xff', 'f', '\0'}을 보유하는 char[3]임

문자열 리터럴은 배열 초기화 에 사용될 수 있으며, 배열의 크기가 문자열 리터럴의 크기보다 하나 작은 경우 널 종결자는 무시됩니다:

char a1[] = "abc"; // a1은 char[4]로 {'a', 'b', 'c', '\0'}을 보유
char a2[4] = "abc"; // a2는 char[4]로 {'a', 'b', 'c', '\0'}을 보유
char a3[3] = "abc"; // a3는 char[3]로 {'a', 'b', 'c'}을 보유

문자 문자열 리터럴 (1) 과 와이드 문자열 리터럴 (5) 의 인코딩은 구현에 따라 정의됩니다. 예를 들어, gcc는 명령줄 옵션 - fexec - charset - fwide - exec - charset 으로 이를 선택합니다.

C11에서는 혼합 와이드 문자열 리터럴 연결이 허용되었지만, 거의 모든 컴파일러가 이러한 연결을 거부하며 (알려진 유일한 예외는 SDCC 임), 사용 경험도 알려져 있지 않습니다. 결과적으로 C23에서는 혼합 와이드 문자열 리터럴 연결 허용이 제거되었습니다.

예제

#include <inttypes.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <uchar.h>
int main(void)
{
    char s1[] = "a猫🍌"; // or "a\u732B\U0001F34C"
#if __STDC_VERSION__ >= 202311L
    char8_t
#else
    char
#endif
    s2[] = u8"a猫🍌";
    char16_t s3[] = u"a猫🍌";
    char32_t s4[] = U"a猫🍌";
    wchar_t s5[] = L"a猫🍌";
    setlocale(LC_ALL, "en_US.utf8");
    printf("  \"%s\" is a char[%zu] holding     { ", s1, sizeof s1 / sizeof *s1);
    for(size_t n = 0; n < sizeof s1 / sizeof *s1; ++n)
        printf("0x%02X ", +(unsigned char)s1[n]);
    puts("}");
    printf(
#if __STDC_VERSION__ >= 202311L
    "u8\"%s\" is a char8_t[%zu] holding  { "
#else
    "u8\"%s\" is a char[%zu] holding     { "
#endif
, s2, sizeof s2 / sizeof *s2);
    for(size_t n = 0; n < sizeof s2 / sizeof *s2; ++n)
#if __STDC_VERSION__ >= 202311L
       printf("0x%02X ", s2[n]);
#else
       printf("0x%02X ", +(unsigned char)s2[n]);
#endif
    puts("}");
    printf(" u\"a猫🍌\" is a char16_t[%zu] holding { ", sizeof s3 / sizeof *s3);
    for(size_t n = 0; n < sizeof s3 / sizeof *s3; ++n)
       printf("0x%04" PRIXLEAST16" ", s3[n]);
    puts("}");
    printf(" U\"a猫🍌\" is a char32_t[%zu] holding { ", sizeof s4 / sizeof *s4);
    for(size_t n = 0; n < sizeof s4 / sizeof *s4; ++n)
       printf("0x%08" PRIXLEAST32" ", s4[n]);
    puts("}");
    printf(" L\"%ls\" is a wchar_t[%zu] holding  { ", s5, sizeof s5 / sizeof *s5);
    for(size_t n = 0; n < sizeof s5 / sizeof *s5; ++n)
       printf("0x%08X ", (unsigned)s5[n]);
    puts("}");
}

가능한 출력:

  "a猫🍌" is a char[9] holding     { 0x61 0xE7 0x8C 0xAB 0xF0 0x9F 0x8D 0x8C 0x00 }
u8"a猫🍌" is a char[9] holding     { 0x61 0xE7 0x8C 0xAB 0xF0 0x9F 0x8D 0x8C 0x00 }
 u"a猫🍌" is a char16_t[5] holding { 0x0061 0x732B 0xD83C 0xDF4C 0x0000 }
 U"a猫🍌" is a char32_t[4] holding { 0x00000061 0x0000732B 0x0001F34C 0x00000000 }
 L"a猫🍌" is a wchar_t[4] holding  { 0x00000061 0x0000732B 0x0001F34C 0x00000000 }

참고문헌

  • C23 표준 (ISO/IEC 9899:2024):
  • 6.4.5 문자열 리터럴 (p: TBD)
  • C17 표준 (ISO/IEC 9899:2018):
  • 6.4.5 문자열 리터럴 (p: 50-52)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.4.5 문자열 리터럴 (p: 70-72)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.4.5 문자열 리터럴 (p: 62-63)
  • C89/C90 표준 (ISO/IEC 9899:1990):
  • 3.1.4 문자열 리터럴

참고 항목

C++ 문서 에 대한 문자열 리터럴