정수 리터럴
정수 리터럴 (Integer literal)
정수 리터럴(integer literal) 은 정수 타입의 값을 표현식에 직접 쓸 수 있게 해 주는 리터럴이에요.
출처: cppreference
본문
문법 (Syntax)
정수 리터럴은 다음 형태를 가져요.
decimal-literal integer-suffix(optional) (1)
octal-literal integer-suffix(optional) (2)
hex-literal integer-suffix(optional) (3)
binary-literal integer-suffix(optional) (4) (C++14부터)
여기서
- decimal-literal 은 0이 아닌 십진 숫자(1
9) 뒤에 십진 숫자(09)가 0개 이상 오는 것 - octal-literal 은 숫자 0 뒤에 8진 숫자(0~7)가 0개 이상 오는 것
- hex-literal 은
0x또는0X문자 시퀀스 뒤에 16진 숫자(0-9, a, A, b, B, c, C, d, D, e, E, f, F)가 1개 이상 오는 것 - binary-literal 은
0b또는0B문자 시퀀스 뒤에 이진 숫자(0, 1)가 1개 이상 오는 것
integer-suffix 는 제공되면 다음 중 하나 또는 둘 다를 포함할 수 있어요 (둘 다 제공되면 아무 순서로 와도 돼요).
- unsigned-suffix (
u또는U문자) - 다음 중 하나:
- long-suffix (
l또는L문자) - long-long-suffix (
ll또는LL문자 시퀀스) (C++11부터) - size-suffix (
z또는Z문자) (C++23부터)
- long-suffix (
선택적 단일 따옴표 '를 숫자 사이에 구분자로 넣을 수 있어요. 리터럴 값을 정할 때는 무시돼요. (C++14부터)
정수 리터럴은 (어떤 리터럴이든) 기본 표현식(primary expression)이에요.
설명 (Explanation)
- 십진 정수 리터럴 (밑 10)
- 8진 정수 리터럴 (밑 8)
- 16진 정수 리터럴 (밑 16,
'a'15를 나타냄)'f'문자는 십진 10 - 이진 정수 리터럴 (밑 2)
정수 리터럴의 첫 숫자가 가장 중요한 자리예요.
예시. 다음 변수들은 같은 값으로 초기화돼요.
int d = 42;
int o = 052;
int x = 0x2a;
int X = 0X2A;
int b = 0b101010; // C++14
예시. 다음 변수들도 같은 값으로 초기화돼요.
unsigned long long l1 = 18446744073709550592ull; // C++11
unsigned long long l2 = 18'446'744'073'709'550'592llu; // C++14
unsigned long long l3 = 1844'6744'0737'0955'0592uLL; // C++14
unsigned long long l4 = 184467'440737'0'95505'92LLU; // C++14
리터럴의 타입 (The type of the literal)
정수 리터럴의 타입은 값이 들어갈 수 있는 목록 중 첫 번째 타입이에요. 목록은 숫자 밑과 어떤 integer-suffix 를 썼는지에 따라 달라요.
| 접미사 | 십진 밑 | 이진·8진·16진 밑 |
|---|---|---|
| (없음) | int, long int, long long int(C++11부터) |
int, unsigned int, long int, unsigned long int, long long int(C++11부터), unsigned long long int(C++11부터) |
u 또는 U |
unsigned int, unsigned long int, unsigned long long int(C++11부터) |
unsigned int, unsigned long int, unsigned long long int(C++11부터) |
l 또는 L |
long int, unsigned long int(C++11까지), long long int(C++11부터) |
long int, unsigned long int, long long int(C++11부터), unsigned long long int(C++11부터) |
l/L와 u/U 둘 다 |
unsigned long int, unsigned long long int(C++11부터) |
unsigned long int, unsigned long long int(C++11부터) |
ll 또는 LL |
long long int(C++11부터) |
long long int(C++11부터), unsigned long long int(C++11부터) |
ll/LL와 u/U 둘 다 |
unsigned long long int(C++11부터) |
unsigned long long int(C++11부터) |
z 또는 Z |
std::size_t의 부호 있는 버전(C++23부터) |
std::size_t의 부호 있는 버전(C++23부터) / std::size_t(C++23부터) |
z/Z와 u/U 둘 다 |
std::size_t(C++23부터) |
std::size_t(C++23부터) |
size-suffix 가 없는(C++23부터) 정수 리터럴의 값이 접미사/밑 조합이 허용하는 어떤 타입에도 들어가지 못할 때, 컴파일러가 리터럴 값을 표현할 수 있는 확장 정수 타입(__int128 같은)을 지원하면 그 리터럴에 그 확장 정수 타입을 줄 수 있어요. 그렇지 않으면 프로그램은 규칙 위반이에요.
참고 (Notes)
- 정수 리터럴의 문자는 대소문자를 구분하지 않아요.
0xDeAdBeEfU와0XdeadBEEFu는 같은 수를 나타내요 (long-long-suffix 는 하나의 예외로ll또는LL만 되고lL이나Ll은 안 돼요, C++11부터). - 음의 정수 리터럴은 없어요.
-1같은 표현식은 단항 마이너스 연산자를 리터럴이 나타내는 값에 적용하는 것이고 여기엔 암시적 타입 변환이 개입할 수 있어요. - C99 이전의 C(C++은 아님)에서는
long int에 안 들어가는 접미사 없는 십진 값이unsigned long int타입을 가질 수 있었어요. #if나#elif의 제어 표현식에서 쓰일 때 모든 부호 있는 정수 상수는std::intmax_t타입이고 모든 부호 없는 정수 상수는std::uintmax_t타입인 것처럼 행동해요. (C++11부터)- 최대 먹기(maximal munch) 때문에,
e와E로 끝나는 16진 정수 리터럴은 뒤에 연산자+나-가 오면 연산자와 공백이나 괄호로 분리해야 해요.
auto x = 0xE+2.0; // error
auto y = 0xa+2.0; // OK
auto z = 0xE +2.0; // OK
auto q = (0xE)+2.0; // OK
그렇지 않으면 단일 무효한 전처리 숫자 토큰이 만들어져 이후 분석이 실패해요.
기능 테스트 매크로: __cpp_binary_literals(201304L, C++14, 이진 리터럴), __cpp_size_t_suffix(202011L, C++23, std::size_t와 그 부호 있는 버전의 리터럴 접미사).
예제 (Example)
#include <cstddef>
#include <iostream>
#include <type_traits>
int main()
{
std::cout << 123 << '\n'
<< 0123 << '\n'
<< 0x123 << '\n'
<< 0b10 << '\n'
<< 12345678901234567890ull << '\n'
<< 12345678901234567890u << '\n'; // the type is unsigned long long
// even without a long long suffix
// std::cout << -9223372036854775808 << '\n'; // error: the value
// 9223372036854775808 cannot fit in signed long long, which is the
// biggest type allowed for unsuffixed decimal integer literal
std::cout << -9223372036854775808u << '\n'; // unary minus applied to unsigned
// value subtracts it from 2^64, this gives 9223372036854775808
std::cout << -9223372036854775807 - 1 << '\n'; // correct way to calculate
// the value -9223372036854775808
#if __cpp_size_t_suffix >= 202011L // C++23
static_assert(std::is_same_v<decltype(0UZ), std::size_t>);
static_assert(std::is_same_v<decltype(0Z), std::make_signed_t<std::size_t>>);
#endif
}
출력:
123
83
291
2
12345678901234567890
12345678901234567890
9223372036854775808
-9223372036854775808
결함 보고 (Defect reports)
동작 변경 결함 보고로 size-suffix 가 있는 정수 리터럴이 확장 정수 타입을 가질 수 있었던 것을, 너무 크면 규칙 위반이 되도록 고쳤어요 (CWG 2698, C++23).