문자 상수(Character constant) — 'a'부터 L'貓'까지
문자 상수(Character constant) — 'a'부터 L'貓'까지
코드에서 'a'처럼 따옴표로 감싼 문자를 쓰곤 하죠. 이것이 **문자 상수(character constant)**예요. 그런데 C의 문자 상수는 한 가지 형태만 있는 게 아니에요. u8'…', u'…', U'…', L'…' 같은 각종 프리픽스가 붙을 수 있고, 각 형태는 서로 다른 타입과 인코딩 규칙을 가져요. 이 문서는 문자 상수의 다양한 형태와 그 타입·값 규칙을 정리해요.
출처: cppreference
본문
문법(Syntax)
'c-char' (1)
u8'c-char' (2) (since C23)
u'c-char' (3) (since C11)
U'c-char' (4) (since C11)
L'c-char' (5)
'c-char-sequence' (6)
L'c-char-sequence' (7)
u'c-char-sequence' (8) (since C11, removed in C23)
U'c-char-sequence' (9) (since C11, removed in C23)
여기서 c-char는 다음 중 하나예요.
- 기본 소스 문자 집합의 문자 중에서 작은따옴표(
'), 백슬래시(\), 줄바꿈 문자를 뺀 문자. - 이스케이프 시퀀스: 특수 문자 이스케이프
\'\"\?\\\a\b\f\n\r\t\v, 16진 이스케이프\x..., 8진 이스케이프\...(이스케이프 시퀀스 문서에서 정의한 대로). - 유니버설 문자 이름(universal character name)
\u...또는\U...(C99부터).
c-char-sequence는 두 개 이상의 c-char로 이뤄진 수열이에요.
-
단일 바이트 정수 문자 상수, 예:
'a','\n','\13'.int타입이고, 실행 문자 집합에서c-char의 표현을char타입 값으로, 다시int로 매핑한 값과 같아요.c-char가 실행 문자 집합에서 단일 바이트로 표현될 수 없으면 값은 구현 정의예요. -
UTF-8 문자 상수, 예:
u8'a'.char8_t타입이고,c-char가 단일 UTF-8 코드 단위로 표현 가능할 때(즉 0x0–0x7F 범위) ISO 10646 코드 포인트 값과 같아요. 단일 UTF-8 코드 단위로 표현할 수 없으면 프로그램은 ill-formed예요. -
16비트 와이드 문자 상수, 예:
u'貓'(u'🍌'은 아님 —u'\U0001f34c').char16_t타입이고,mbrtoc16이 만드는 16비트 인코딩(보통 UTF-16)에서c-char의 값과 같아요. 표현할 수 없거나 하나 이상의 16비트 문자로 매핑되면 값은 구현 정의예요. C23까지 동작. (C23부터는 아래 3) 항 참고) -
32비트 와이드 문자 상수, 예:
U'貓'또는U'🍌'.char32_t타입이고, 32비트 인코딩(보통 UTF-32)에서c-char의 값과 같아요. 표현할 수 없거나 하나 이상의 32비트 문자로 매핑되면 값은 구현 정의예요. C23까지 동작.
C23부터는 위 3)·4) 항이 다음과 같이 바뀌어요.
UTF-16 문자 상수, 예:
u'貓'(u'🍌'은 아님 —u'\U0001f34c').char16_t타입이고,c-char가 단일 UTF-16 코드 단위로 표현 가능할 때(0x0–0xD7FF 또는 0xE000–0xFFFF) ISO 10646 코드 포인트 값과 같아요. 단일 UTF-16 코드 단위로 표현할 수 없으면 프로그램은 ill-formed예요.UTF-32 문자 상수, 예:
U'貓'또는U'🍌'.char32_t타입이고,c-char가 단일 UTF-32 코드 단위로 표현 가능할 때(0x0–0xD7FF 또는 0xE000–0x10FFFF) ISO 10646 코드 포인트 값과 같아요. 단일 UTF-32 코드 단위로 표현할 수 없으면 프로그램은 ill-formed예요.
-
와이드 문자 상수, 예:
L'β'또는L'貓'.wchar_t타입이고, 실행 와이드 문자 집합(즉mbtowc가 만드는 값)에서c-char의 값과 같아요. 표현할 수 없거나 하나 이상의 와이드 문자로 매핑되면(예:wchar_t가 16비트인 Windows에서 비-BMP 값) 값은 구현 정의예요. -
다중 문자 상수(multicharacter constant), 예:
'AB'.int타입이고 구현 정의 값을 가져요. -
와이드 다중 문자 상수, 예:
L'AB'.wchar_t타입이고 구현 정의 값을 가져요. -
16비트 다중 문자 상수, 예:
u'CD'.char16_t타입이고 구현 정의 값을 가져요. -
32비트 다중 문자 상수, 예:
U'XY'.char32_t타입이고 구현 정의 값을 가져요.
참고(Notes)
다중 문자 상수는 C가 B 프로그래밍 언어에서 물려받았어요. C 표준이 규정하지는 않지만, 대부분의 컴파일러( MSVC는 주목할 만한 예외)는 B에서처럼 구현해요. 즉 상수 안 각 char의 값이 결과 정수의 연속된 바이트를, big-endian zero-padded right-adjusted 순서로 초기화해요. 예를 들어 '\1'은 0x00000001, '\1\2\3\4'는 0x01020304가 돼요.
C++에서는 인코딩 가능한 일반 문자 리터럴이 int가 아니라 char 타입이에요.
정수 상수와 달리, char가 부호 있으면 문자 상수는 음수 값일 수 있어요. 그런 구현에서 '\xFF'는 값이 -1인 int예요.
#if나 #elif의 제어 표현식에서 쓰일 때, 문자 상수는 소스 문자 집합, 실행 문자 집합, 또는 어떤 다른 구현 정의 문자 집합으로 해석될 수 있어요.
16/32비트 다중 문자 상수는 널리 지원되지 않고 C23에서 제거됐어요. 몇몇 일반적인 구현(예: clang)은 아예 받아들이지 않아요.
예제(Example)
#include <stddef.h>
#include <stdio.h>
#include <uchar.h>
int main (void)
{
printf("constant value \n");
printf("-------- ----------\n");
// integer character constants,
int c1='a'; printf("'a':\t %#010x\n", c1);
int c2='🍌'; printf("'🍌':\t %#010x\n\n", c2); // implementation-defined
// multicharacter constant
int c3='ab'; printf("'ab':\t %#010x\n\n", c3); // implementation-defined
// 16-bit wide character constants
char16_t uc1 = u'a'; printf("'a':\t %#010x\n", (int)uc1);
char16_t uc2 = u'¢'; printf("'¢':\t %#010x\n", (int)uc2);
char16_t uc3 = u'猫'; printf("'猫':\t %#010x\n", (int)uc3);
// implementation-defined (🍌 maps to two 16-bit characters)
char16_t uc4 = u'🍌'; printf("'🍌':\t %#010x\n\n", (int)uc4);
// 32-bit wide character constants
char32_t Uc1 = U'a'; printf("'a':\t %#010x\n", (int)Uc1);
char32_t Uc2 = U'¢'; printf("'¢':\t %#010x\n", (int)Uc2);
char32_t Uc3 = U'猫'; printf("'猫':\t %#010x\n", (int)Uc3);
char32_t Uc4 = U'🍌'; printf("'🍌':\t %#010x\n\n", (int)Uc4);
// wide character constants
wchar_t wc1 = L'a'; printf("'a':\t %#010x\n", (int)wc1);
wchar_t wc2 = L'¢'; printf("'¢':\t %#010x\n", (int)wc2);
wchar_t wc3 = L'猫'; printf("'猫':\t %#010x\n", (int)wc3);
wchar_t wc4 = L'🍌'; printf("'🍌':\t %#010x\n\n", (int)wc4);
}
가능한 출력:
constant value
-------- ----------
'a': 0x00000061
'🍌': 0xf09f8d8c
'ab': 0x00006162
'a': 0x00000061
'¢': 0x000000a2
'猫': 0x0000732b
'🍌': 0x0000df4c
'a': 0x00000061
'¢': 0x000000a2
'猫': 0x0000732b
'🍌': 0x0001f34c
'a': 0x00000061
'¢': 0x000000a2
'猫': 0x0000732b
'🍌': 0x0001f34c
u'🍌'(16비트)는 이미지 이모지가 하나의 16비트 문자로 표현이 안 돼서 값이 0x0000df4c처럼 조정된 값으로 나오는 반면, U'🍌'(32비트)는 그대로 0x0001f34c가 돼요.
더 알아보기
- 문자열 리터럴에 대한 String literal 문서에서 프리픽스별 인코딩을 함께 다뤄요.
- 이스케이프 시퀀스와 유니버설 문자 이름에 대한 문서를 참고해요.