정수 상수

정수 상수 (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부터)

| 선택적 단일 따옴표(')를 숫자 사이에 구분자로 넣을 수 있어요. 컴파일러는 이를 무시해요. | (C23부터) |

설명

  1. 십진 정수 상수(밑 10, 첫 자리 숫자가 최상위).
  2. 8진 정수 상수(밑 8, 첫 자리 숫자가 최상위).
  3. 16진 정수 상수(밑 16, 첫 자리 숫자가 최상위, af는 십진 값 1015).
  4. 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 십진 밑 다른 밑
접미사 없음 int
long int
unsigned long int(C99 이전)
long long int(C99부터)
int
unsigned int
long int
unsigned long int
long long int(C99부터)
unsigned long long int(C99부터)
u 또는 U unsigned int
unsigned long int
unsigned long long int(C99부터)
unsigned int
unsigned long int
unsigned long long int(C99부터)
l 또는 L long int
unsigned long int(C99 이전)
long long int(C99부터)
long int
unsigned long int
long long int(C99부터)
unsigned long long int(C99부터)
l/Lu/U 둘 다 unsigned long int
unsigned long long int(C99부터)
unsigned long int
unsigned long long int(C99부터)
ll 또는 LL long long int(C99부터) long long int(C99부터)
unsigned long long int(C99부터)
ll/LLu/U 둘 다 unsigned long long int(C99부터) unsigned long long int(C99부터)
wb 또는 WB _BitInt(N) — 값과 부호 비트를 수용하는 1보다 큰 가장 작은 너비 N(C23부터) _BitInt(N) — 값과 부호 비트를 수용하는 1보다 큰 가장 작은 너비 N(C23부터)
wb/WBu/U 둘 다 unsigned _BitInt(N) — 값을 수용하는 0보다 큰 가장 작은 너비 N(C23부터) unsigned _BitInt(N) — 값을 수용하는 0보다 큰 가장 작은 너비 N(C23부터)

정수 상수의 값이 suffix/밑 조합으로 허용된 어떤 타입에도 너무 커서 안 들어가는데, wb, WB, uwb, UWB 접미사가 없고(C23부터) 컴파일러가 확장 정수 타입(__int128 같은)을 지원하면, 그 상수는 확장 정수 타입을 가질 수 있어요. 그렇지 않으면 프로그램은 ill-formed예요.

참고

정수 상수의 문자는 대소문자를 구분하지 않아요. 0xDeAdBaBeU0XdeadBABEu는 같은 수를 나타내요(한 가지 예외는 long-long-suffix로, ll이나 LL이지 lL이나 Ll이 아니에요)(C99부터).

음의 정수 상수는 없어요. -1 같은 표현식은 상수가 나타내는 값에 단항 마이너스 연산자를 적용한 것이에요.

| #if#elif의 제어 표현식에서 사용되면 모든 부호 있는 정수 상수는 마치 타입 intmax_t를 가진 것처럼, 모든 부호 없는 정수 상수는 마치 타입 uintmax_t를 가진 것처럼 동작해요. | (C99부터) |

정수 상수는 정수 상수 표현식(integer constant expression)에 사용될 수 있어요.

최대 꿀꺽(maximal munch) 때문에, eE로 끝나는 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

더 알아보기