상수 표현식

상수 표현식 (Constant expressions)

C에는 상수 표현식(constant expression) 이라고 불리는 여러 종류의 표현식이 있어요. 이름만 봐서는 하나인 것 같지만, 실제로는 용도에 따라 조금씩 다른 종류가 있고, 각각 어디에 쓸 수 있는지가 정해져 있어요. 이 페이지에서 그 종류들을 정리해 볼게요.

출처: cppreference

본문

전처리기 상수 표현식 (Preprocessor constant expression)

#if나 #elif 다음에 오는 표현식은 다음으로 확장되어야 해요.

#if 표현식에서 평가될 때 문자 상수는 소스 문자 집합, 실행 문자 집합, 또는 다른 구현 정의 문자 집합으로 해석될 수 있어요.

#if 표현식의 정수 산술은 부호 있는 타입에는 intmax_t의, 부호 없는 타입에는 uintmax_t의 의미론을 사용해 수행돼요. (since C99)

정수 상수 표현식 (Integer constant expression)

정수 상수 표현식은 다음만으로 구성된 표현식이에요.

_Alignof(until C23) / alignof(since C23) 연산자 (since C11)
정수 타입이거나, 산술 타입이면서 캐스트의 즉시 피연산자인 이름 있는 및 복합 리터럴 상수 (since C23)

정수 상수 표현식은 컴파일 타임에 평가돼요. 다음 문맥들은 정수 상수 표현식 으로 알려진 표현식을 요구해요.

배열 지정자(designator)의 인덱스 (since C99)
_Static_assert의 첫 번째 인자, _Alignof(until C23) / alignof(since C23)의 정수 인자 (since C11)
비트 정밀 정수 타입(_BitInt(N))의 비트 수 N (since C23)

정적 초기화식 (Static initializer)

정적 및 thread_local 저장 기간의 객체나, constexpr 저장 클래스 지정자(since C23)로 선언된 객체의 초기화에 쓰이는 표현식은 문자열 리터럴이거나 다음 중 하나일 수 있는 표현식이어야 해요.

_Alignof(until C23) / alignof(since C23) 연산자 (since C11)
산술 타입의 이름 있는 또는 복합 리터럴 상수 (since C23)
5) 이름 있는 상수(named constant) — 열거형 상수이거나, true·false·nullptr 중 하나인 미리 정의된 상수이거나, constexpr 저장 클래스 지정자로 선언되고 객체 타입을 가진 식별자, 또는 구조체·공용체 타입의 이름 있는 상수에 . 멤버 접근 연산자를 적용한 후위 표현식(재귀적으로도 가능). 6) 복합 리터럴 상수(compound literal constant) — constexpr 저장 클래스 지정자를 가진 복합 리터럴, 또는 구조체·공용체 타입의 복합 리터럴 상수에 . 멤버 접근 연산자를 적용한 후위 표현식(재귀적으로도 가능). 구조체 또는 공용체 상수는 각각 구조체 또는 공용체 타입의 이름 있는 상수나 복합 리터럴 상수예요. . 멤버 접근 연산자가 공용체 상수의 멤버에 접근한다면, 접근하는 멤버는 그 공용체 상수의 초기화자에 의해 초기화되는 멤버와 같아야 해요. (since C23)

정수 상수 표현식과 달리, 정적 초기화식은 반드시 컴파일 타임에 평가될 필요는 없어요. 컴파일러는 그런 초기화식을 프로그램 시작 전에 호출되는 실행 코드로 바꿀 자유가 있어요.

static int i = 2 || 1 / 0; // i를 값 1로 초기화
이 섹션은 불완전해요. 사유: 다른 미니 예제 없음

부동소수점 정적 초기화식의 값은 런타임에 실행된 같은 표현식의 값보다 덜 정확할 수는 없지만, 더 정확할 수도 있어요.

부동소수점 상수 표현식 (Floating-point constant expressions)

정적 초기화식에 사용되지 않는 부동소수점 타입의 산술 상수 표현식은 항상 런타임 중에 평가된 것처럼 동작해요. 현재 반올림(FENV_ACCESS가 켜져 있다면)의 영향을 받고, math_errhandling에 지정된 대로 오류를 보고해요.

void f(void)
{
#pragma STDC FENV_ACCESS ON
    static float x = 0.0 / 0.0; // 정적 초기화식: 예외를 발생시키지 않음
    float w[] = { 0.0 / 0.0 };  // 예외를 발생시킴
    float y = 0.0 / 0.0;        // 예외를 발생시킴
    double z = 0.0 / 0.0;       // 예외를 발생시킴
}

주의 (Notes)

표현식이 그 타입으로 표현할 수 없는 값으로 평가된다면, 상수 표현식으로 사용할 수 없어요.

구현이 다른 형태의 상수 표현식을 받아들일 수도 있어요. 그러나 그런 상수 표현식은 정수 상수 표현식, 산술 상수 표현식, 또는 주소 상수 표현식으로 간주되지 않으므로, 그런 종류의 상수 표현식을 요구하는 문맥에서 쓸 수 없어요. 예를 들어 int arr[(int)+1.0];는 VLA를 선언해요.

더 알아보기