열거형

열거형 (Enumerations)

이름 붙인 상수들을 한데 묶어 타입으로 만들고 싶을 때가 있어요. 예를 들어 색깔, 상태, 채널 같은 것들이 그렇죠. 열거형(enumeration) 은 그런 이름 있는 상수들(열거형 상수)을 가진 별개의 타입이에요. 이 페이지에서 열거형을 선언하고 쓰는 방법을 자세히 볼게요.

출처: cppreference

본문

열거형(enumerated type) 은 그 값이 기반 타입(underlying type) (아래 참조)의 값인 별개의 타입이에요. 그 기반 타입은 명시적으로 이름 붙은 상수들(열거형 상수)의 값을 포함해요.

구문 (Syntax)

열거형은 선언 문법에서 타입 지정자로 쓰이는 다음 열거 지정자(enumeration specifier) 로 선언돼요.

enum attr-spec-seq (optional) identifier (optional) { enumerator-list }          (1)
enum attr-spec-seq (optional) identifier (optional) : type { enumerator-list }   (2)  (since C23)

여기서 enumerator-list는 열거자의 쉼표로 구분된 목록(trailing comma 허용)(since C99)이며, 각 열거자는 다음 형태를 가져요.

enumeration-constant attr-spec-seq (optional)                         (1)
enumeration-constant attr-spec-seq (optional) = constant-expression   (2)

여기서

항목 설명
identifier, enumeration-constant 이 선언이 도입하는 식별자
constant-expression 값이 int(until C23) 타입의 값으로 표현 가능한 정수 상수 표현식. 열거형이 고정 기반 타입을 가지면 그 타입(since C23)의 값으로 표현 가능해야 함
attr-spec-seq (C23) enum 뒤에 나타나면 열거형 전체에, enumeration-constant 뒤에 나타나면 그 열거자에 적용되는 선택적인 속성 목록

structunion과 마찬가지로, 열거형과 하나 이상의 열거형 상수를 도입하는 선언은 그 타입이나 그로부터 파생된 타입의 객체를 하나 이상 선언할 수도 있어요.

enum color { RED, GREEN, BLUE } c = RED, *cp = &c;
// 타입 enum color를 도입
// 정수 상수 RED, GREEN, BLUE를 도입
// enum color 타입의 객체 c를 도입
// enum color에 대한 포인터 타입의 객체 cp를 도입

설명 (Explanation)

열거 지정자 본문에 나타나는 각 열거형 상수는 둘러싸는 스코프에서 타입 int(until C23)의 정수 상수가 되고, 정수 상수가 필요한 곳이면 어디든 쓸 수 있어요(예: case 레이블, 비-VLA 배열 크기).

열거자 목록에서 각 열거형 상수를 처리하는 동안, 그 열거형 상수의 타입은: 같은 열거형 상수의 재선언이라면 이전에 선언된 타입; 고정 기반 타입을 가진 열거형이라면 그 열거형 타입; 열거자 목록에 이전 열거형 상수가 없고 정의하는 정수 상수 표현식을 가진 명시적 =가 없다면 int; =로 명시적으로 주어졌고 그 정수 상수 표현식의 값이 int로 표현 가능하다면 int; =로 명시적으로 주어졌고 그 값이 int로 표현 불가능하다면 그 정수 상수 표현식의 타입; 또는 1을 더한 마지막 열거형 상수의 값의 타입. 그런 정수 상수 표현식이 이전 열거형 상수의 값에 1을 더한 결과를 오버플로하거나 감싸버리면, 타입은 다음 중 하나가 돼요: 이전 열거형 상수의 값에 1을 더한 값을 표현할 수 있는, 적절히 크기가 조정된 부호 있는 정수 타입(비트 정밀 부호 있는 정수 타입 제외); 또는 이전 열거형 상수의 값에 1을 더한 값을 표현할 수 있는, 적절히 크기가 조정된 부호 없는 정수 타입(비트 정밀 부호 없는 정수 타입 제외). 더해지는 이전 열거형 상수가 부호 있는 정수 타입이면 부호 있는 정수 타입이, 부호 없는 정수 타입이면 부호 없는 정수 타입이 선택돼요. 새 값을 표현할 수 있는 적절한 크기의 정수 타입이 없다면, 그 열거형은 그 값들을 모두 표현할 수 있는 타입을 가지지 못해요. (since C23)
enum color { RED, GREEN, BLUE } r = RED;
switch(r)
{
case RED:
    puts("red");
    break;
case GREEN:
    puts("green");
    break;
case BLUE:
    puts("blue");
    break;
}

열거형 상수 뒤에 = constant-expression이 오면 그 값은 그 상수 표현식의 값이에요. = 없이 열거형 상수가 이어지면 그 값은 같은 열거형에서 이전 열거자의 값보다 1 큰 값이에요. 첫 열거자의 값(= constant-expression을 쓰지 않는다면)은 0이에요.

enum Foo { A, B, C = 10, D, E = 1, F, G = F + C };
// A=0, B=1, C=10, D=11, E=1, F=2, G=12

식별자 자체를 사용하면 그것은 태그 네임스페이스에서 열거형 타입의 이름이 되고, 키워드 enum의 사용을 요구해요(typedef로 일반 네임스페이스에 넣지 않는 한).

enum color { RED, GREEN, BLUE };
enum color r = RED; // OK
// color x = GREEN; // 오류: color는 일반 네임스페이스에 없음
typedef enum color color_t;
color_t x = GREEN; // OK

고정 기반 타입이 없는(since C23) 각 열거형은 char, 부호 있는 정수 타입, 또는 부호 없는 정수 타입(bool과 비트 정밀 정수 타입 제외)(since C23) 중 하나와 호환(compatible)이에요. 어떤 타입이 주어진 열거형과 호환되는지는 구현 정의지만, 어느 것이든 그 열거형의 모든 열거자 값을 표현할 수 있어야 해요. 고정 기반 타입을 가진 모든 열거형에 대해서는, 열거형이 그 열거형의 기반 타입과 호환돼요. (since C23)

완료 시점에 고정 기반 타입이 없는 열거형의 열거형 멤버 타입은: 그 열거형의 모든 값이 int로 표현 가능하다면 int; 또는 그 열거형 타입. (since C23)
모든 열거형은 기반 타입을 가져요. 기반 타입은 enum-type-specifier로 명시적으로 지정될 수 있고, 그러면 그것이 고정 기반 타입이에요. 명시적으로 지정되지 않으면 기반 타입은 열거형의 호환 타입, 즉 부호 있거나 부호 없는 정수 타입이나 char예요. (since C23)

열거형은 정수 타입이므로, 다른 정수 타입을 쓸 수 있는 곳이면 어디든 쓸 수 있어요. 암시적 변환산술 연산자 포함해서요.

enum { ONE = 1, TWO } e;
long n = ONE; // 승격(promotion)
double d = ONE; // 변환(conversion)
e = 1.2; // 변환, e는 이제 ONE
e = e + 1; // e는 이제 TWO

주의 (Notes)

structunion과 달리, C에는 전방 선언된(forward-declared) 열거형이 없어요.

enum Color; // 오류: C에는 열거형 전방 선언이 없음
enum Color { RED, GREEN, BLUE };

열거형은 #define보다 더 편리하고 구조화된 방식으로 이름 있는 상수의 선언을 허용해요. 디버거에서 보이고, 스코프 규칙을 따르며, 타입 시스템에 참여해요.

#define TEN 10
struct S { int x : TEN; }; // OK

또는

enum { TEN = 10 };
struct S { int x : TEN; }; // 역시 OK

C23부터 같은 목적으로 constexpr을 쓸 수 있어요.

constexpr int TEN = 10;
struct S { int x : TEN; }; // 역시 OK

게다가 structunion이 C에서 스코프를 설정하지 않으므로, 열거형과 그 열거형 상수는 구조체·공용체의 멤버 지정부에서 도입될 수 있고, 그 후 그 스코프는 전자와 같아요.

struct Element
{
    int z;
    enum State { SOLID, LIQUID, GAS, PLASMA } state;
} oxygen = { 8, GAS };

// 타입 enum State와 그 열거형 상수는 여기서도 보임. 예:
void foo(void)
{
    enum State e = LIQUID; // OK
    printf("%d %d %d ", e, oxygen.state, PLASMA); // 1 2 3을 출력
}

예제 (Example)

#include <stdio.h>

int main(void)
{
    enum TV { FOX = 11, CNN = 25, ESPN = 15, HBO = 22, MAX = 30, NBC = 32 };

    printf("List of cable stations:\n");
    printf(" FOX: \t%2d\n", FOX);
    printf(" HBO: \t%2d\n", HBO);
    printf(" MAX: \t%2d\n", MAX);
}

출력:

List of cable stations:
 FOX:   11
 HBO:   22
 MAX:   30

키워드 (Keywords)

enum

더 알아보기