정수 상수
정수 상수 (integer constant)
코드 안에 숫자 값을 직접 적어 넣는 걸 상수라고 해요. C에서는 같은 값이라도 쓰는 진법에 따라 42, 052, 0x2a 같은 여러 형태로 적을 수 있고, 뒤에 접미사를 붙여 타입도 지정할 수 있어요.
출처: cppreference
본문
정수 상수는 비-lvalue(non-lvalue) 표현식으로, 다음과 같은 형태를 가져요:
| decimal-constant integer-suffix(optional) | (1) | |
| octal-constant integer-suffix(optional) | (2) | |
| hex-constant integer-suffix(optional) | (3) | |
| binary-constant integer-suffix(optional) | (4) | (C23부터) |
여기서
- decimal-constant는 0이 아닌 십진 숫자(
19) 다음에 0개 이상의 십진 숫자(09)가 따라오는 것. - octal-constant는 숫자 0 다음에 0개 이상의 8진 숫자(
0~7)가 따라오는 것. - hex-constant는
0x또는0X다음에 하나 이상의 16진 숫자(09,af,A~F)가 따라오는 것. - binary-constant는
0b또는0B다음에 하나 이상의 2진 숫자(0,1)가 따라오는 것. - integer-suffix가 제공되면 다음 중 하나를 포함할 수 있어요(부호 없는 접미사는 다른 것들과 결합할 수 있고, 두 접미사를 쓰면 어떤 순서로든 나타날 수 있어요):
- unsigned-suffix: 문자
u또는U - long-suffix(
l또는L) 또는 long-long-suffix(ll또는LL)(C99부터) - bit-precise-int-suffix: 문자 나열
wb또는WB(C23부터)
- unsigned-suffix: 문자
| 선택적 단일 따옴표(')를 숫자 사이에 구분자로 넣을 수 있어요. 컴파일러는 이를 무시해요. | (C23부터) |
설명
- 십진 정수 상수(밑 10, 첫 자리 숫자가 최상위).
- 8진 정수 상수(밑 8, 첫 자리 숫자가 최상위).
- 16진 정수 상수(밑 16, 첫 자리 숫자가 최상위,
a15).f는 십진 값 10 - 2진 정수 상수(밑 2, 첫 자리 숫자가 최상위).
다음 변수들은 모두 같은 값으로 초기화돼요:
int d = 42;
int o = 052;
int x = 0x2a;
int X = 0X2A;
int b = 0b101010; // C23
다음 변수들도 모두 같은 값으로 초기화돼요:
unsigned long long l1 = 18446744073709550592ull; // C99
unsigned long long l2 = 18'446'744'073'709'550'592llu; // C23
unsigned long long l3 = 1844'6744'0737'0955'0592uLL; // C23
unsigned long long l4 = 184467'440737'0'95505'92LLU; // C23
정수 상수의 타입
정수 상수의 타입은 그 값이 들어갈 수 있는 타입 중 첫 번째 타입이에요. 후보 타입 목록은 숫자 밑(base)과 어떤 integer-suffix를 썼는지에 따라 달라져요.
| suffix | 십진 밑 | 다른 밑 |
|---|---|---|
| 접미사 없음 | intlong intunsigned long int(C99 이전)long long int(C99부터) |
intunsigned intlong intunsigned long intlong long int(C99부터)unsigned long long int(C99부터) |
u 또는 U |
unsigned intunsigned long intunsigned long long int(C99부터) |
unsigned intunsigned long intunsigned long long int(C99부터) |
l 또는 L |
long intunsigned long int(C99 이전)long long int(C99부터) |
long intunsigned long intlong long int(C99부터)unsigned long long int(C99부터) |
l/L과 u/U 둘 다 |
unsigned long intunsigned long long int(C99부터) |
unsigned long intunsigned long long int(C99부터) |
ll 또는 LL |
long long int(C99부터) |
long long int(C99부터)unsigned long long int(C99부터) |
ll/LL과 u/U 둘 다 |
unsigned long long int(C99부터) |
unsigned long long int(C99부터) |
wb 또는 WB |
_BitInt(N) — 값과 부호 비트를 수용하는 1보다 큰 가장 작은 너비 N(C23부터) |
_BitInt(N) — 값과 부호 비트를 수용하는 1보다 큰 가장 작은 너비 N(C23부터) |
wb/WB와 u/U 둘 다 |
unsigned _BitInt(N) — 값을 수용하는 0보다 큰 가장 작은 너비 N(C23부터) |
unsigned _BitInt(N) — 값을 수용하는 0보다 큰 가장 작은 너비 N(C23부터) |
정수 상수의 값이 suffix/밑 조합으로 허용된 어떤 타입에도 너무 커서 안 들어가는데, wb, WB, uwb, UWB 접미사가 없고(C23부터) 컴파일러가 확장 정수 타입(__int128 같은)을 지원하면, 그 상수는 확장 정수 타입을 가질 수 있어요. 그렇지 않으면 프로그램은 ill-formed예요.
참고
정수 상수의 문자는 대소문자를 구분하지 않아요. 0xDeAdBaBeU와 0XdeadBABEu는 같은 수를 나타내요(한 가지 예외는 long-long-suffix로, ll이나 LL이지 lL이나 Ll이 아니에요)(C99부터).
음의 정수 상수는 없어요. -1 같은 표현식은 상수가 나타내는 값에 단항 마이너스 연산자를 적용한 것이에요.
| #if나 #elif의 제어 표현식에서 사용되면 모든 부호 있는 정수 상수는 마치 타입 intmax_t를 가진 것처럼, 모든 부호 없는 정수 상수는 마치 타입 uintmax_t를 가진 것처럼 동작해요. | (C99부터) |
정수 상수는 정수 상수 표현식(integer constant expression)에 사용될 수 있어요.
최대 꿀꺽(maximal munch) 때문에, e와 E로 끝나는 16진 정수 상수 뒤에 +나 - 연산자가 오면 원본 코드에서 공백이나 괄호로 연산자와 분리해야 해요:
int x = 0xE+2; // error
int y = 0xa+2; // OK
int z = 0xE +2; // OK
int q = (0xE)+2; // OK
그렇지 않으면 유효하지 않은 전처리 숫자 토큰 하나가 만들어져서 이후 분석이 실패해요.
예제
다양한 진법과 접미사에 대해 정수 상수의 값과 타입이 어떻게 결정되는지 확인해 볼게요. 특히 큰 값에서 어떤 타입으로 맞춰지는지 주목해요.
#include <inttypes.h>
#include <stdio.h>
int main(void)
{
printf("123 = %d\n", 123);
printf("0123 = %d\n", 0123);
printf("0x123 = %d\n", 0x123);
printf("12345678901234567890ull = %llu\n", 12345678901234567890ull);
// the type is a 64-bit type (unsigned long long or possibly unsigned long)
// even without a long suffix
printf("12345678901234567890u = %"PRIu64"\n", 12345678901234567890u );
// printf("%lld\n", -9223372036854775808); // Error:
// the value 9223372036854775808 cannot fit in signed long long, which
// is the biggest type allowed for unsuffixed decimal integer constant
printf("%llu\n", -9223372036854775808ull );
// unary minus applied to unsigned value subtracts it from 2^64,
// this gives unsigned 9223372036854775808
printf("%lld\n", -9223372036854775807ll - 1);
// correct way to form signed value -9223372036854775808
}
출력:
123 = 123
0123 = 83
0x123 = 291
12345678901234567890ull = 12345678901234567890
12345678901234567890u = 12345678901234567890
9223372036854775808
-9223372036854775808
더 알아보기
- 부동소수 리터럴은
floating constant문서에서 다뤄요. - 상수가 등장하는 정수 상수 표현식의 규칙은
constant expression문서를 참고해요.