Angka 0, 1, 2 sebagai kode status terasa kabur—pembaca kode harus menghafal bahwa 2 berarti “rabu” atau “warning”. Makro #define bisa membantu, tetapi tidak tercakup oleh type checker dan tidak berperilaku sebagai tipe. C menyediakan enum untuk mendefinisikan kumpulan konstanta integer bernama yang saling berhubungan secara logis. enum memberi nama bermakna pada angka, sekaligus dapat dipakai sebagai tipe untuk variabel dan parameter. Pada chapter ini kita akan mendefinisikan enum, memahami aturan penomoran otomatis dan eksplisit, serta melihat bagaimana enum dipakai untuk bitmask peran pengguna.

Apa Itu enum #

enum (kependekan dari enumeration) adalah tipe integer yang konstanta-konstantanya diberi nama oleh programmer. Bentuk umumnya: enum <tag> { KONSTAN1, KONSTAN2, ... };. Tanpa inisialisasi eksplisit, konstanta pertama diberi nilai 0, konstanta berikutnya bertambah satu. Kita juga bisa memberi nilai eksplisit dengan KONSTAN = nilai, dan konstanta berikutnya akan bertambah satu dari nilai eksplisit tersebut.

Mekanisme kerja di balik layar: enum sebenarnya adalah alias untuk salah satu tipe integer (biasanya int), sehingga variabel enum bisa dipakai dalam ekspresi aritmatika dan komparasi seperti int. Compiler bebas memilih tipe integer yang cukup untuk merepresentasikan semua nilai konstanta; pada GCC, secara default pakai int. Karena itu, sizeof(enum Hari) umumnya 4 byte, sama dengan sizeof(int).

enum Dasar dengan Penomoran Otomatis #

Kita mulai dengan contoh klasik: enum Hari untuk hari dalam seminggu. Tanpa nilai eksplisit, compiler memberi SENIN = 0, SELASA = 1, dan seterusnya.

c
#include <stdio.h>

/* enum Hari: konstanta integer bernama */
enum Hari {
    SENIN,
    SELASA,
    RABU,
    KAMIS,
    JUMAT,
    SABTU,
    MINGGU
};

/* enum dengan nilai awal eksplisit */
enum Level {
    LEVEL_DEBUG = 0,
    LEVEL_INFO  = 10,
    LEVEL_WARN  = 20,
    LEVEL_ERROR = 30
};

/* enum dengan beberapa nilai eksplisit dan implisit */
enum Status {
    STATUS_NEW = 100,   /* 100 */
    STATUS_WIP,         /* 101 (implisit) */
    STATUS_REVIEW,      /* 102 */
    STATUS_DONE = 200,  /* 200 */
    STATUS_ARCHIVED     /* 201 */
};

int main(void) {
    enum Hari hari = RABU;
    printf("Hari: %d\n", hari);

    /* Iterasi seluruh konstanta enum Hari */
    printf("Semua hari dalam seminggu:\n");
    for (int h = SENIN; h <= MINGGU; h++) {
        printf("  hari ke-%d\n", h);
    }

    /* Bandingkan */
    if (hari == RABU) {
        printf("Hari ini Rabu (nilai %d)\n", RABU);
    }

    /* enum dengan nilai eksplisit */
    printf("\nLevel: DEBUG=%d, INFO=%d, WARN=%d, ERROR=%d\n",
           LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR);

    /* enum Status dengan nilai campuran */
    printf("Status: NEW=%d, WIP=%d, REVIEW=%d, DONE=%d, ARCHIVED=%d\n",
           STATUS_NEW, STATUS_WIP, STATUS_REVIEW, STATUS_DONE, STATUS_ARCHIVED);

    printf("sizeof(enum Hari) = %zu byte\n", sizeof(enum Hari));

    return 0;
}

Output yang Dihasilkan:

text
Hari: 2
Semua hari dalam seminggu:
  hari ke-0
  hari ke-1
  hari ke-2
  hari ke-3
  hari ke-4
  hari ke-5
  hari ke-6
Hari ini Rabu (nilai 2)

Level: DEBUG=0, INFO=10, WARN=20, ERROR=30
Status: NEW=100, WIP=101, REVIEW=102, DONE=200, ARCHIVED=201
sizeof(enum Hari) = 4 byte

Mari kita telaah baris-baris kode di atas:

  1. Baris 5-13 (enum Hari { SENIN, SELASA, ..., MINGGU };): tujuh konstanta tanpa nilai eksplisit. Compiler memberi nilai berturut-turut 0, 1, 2, 3, 4, 5, 6. Karena RABU adalah konstanta ketiga, nilainya 2.
  2. Baris 16-21 (enum Level { LEVEL_DEBUG = 0, ... };): setiap konstanta diberi nilai eksplisit—0, 10, 20, 30. Pola seperti ini umum dipakai untuk log level, di mana ambang batas (misalnya “tampilkan semua log >= WARN”) bisa dibandingkan dengan operator >=.
  3. Baris 24-30 (enum Status { STATUS_NEW = 100, STATUS_WIP, STATUS_REVIEW, STATUS_DONE = 200, STATUS_ARCHIVED };): kombinasi nilai eksplisit dan implisit. STATUS_NEW = 100, lalu STATUS_WIP otomatis 101 (satu lebih dari STATUS_NEW), STATUS_REVIEW 102. Kemudian STATUS_DONE = 200 mengubah basis, sehingga STATUS_ARCHIVED menjadi 201. Pola ini berguna ketika beberapa konstanta berada di rentang berbeda.
  4. Baris 33 (enum Hari hari = RABU;): deklarasi variabel bertipe enum Hari. RABU adalah konstanta enumerator, di-scope ke enum Hari. Kompatibel dengan int untuk operasi aritmatika.
  5. Baris 38-40 (for (int h = SENIN; h <= MINGGU; h++)): iterasi seluruh nilai enum Hari dengan loop for. Karena konstanta berurutan 0..6, loop ini mencakup semuanya. Pola ini tidak akan bekerja dengan enum Status karena nilainya tidak berurutan.
  6. Baris 43-45 (if (hari == RABU)): komparasi variabel enum dengan konstanta enumerator. Lebih bermakna dibanding if (hari == 2).
  7. Baris 56 (sizeof(enum Hari)): hasil 4 byte, sama dengan sizeof(int) pada platform ini. Compiler bebas memilih tipe underlying yang cukup untuk merepresentasikan semua nilai; GCC memilih int secara default.

enum dengan typedef dan Bitmask #

Dalam praktik nyata, enum sering dipadukan dengan typedef agar penulisan lebih ringkas, dan dimanfaatkan untuk bitmask dengan operasi shift.

c
#include <stdio.h>

/* enum dengan typedef untuk kemudahan */
typedef enum {
    MERAH,
    KUNING,
    HIJAU
} LampuLaluLintas;

/* enum untuk peran pengguna dengan bitmask */
typedef enum {
    PERAN_NONE    = 0,
    PERAN_BACA    = 1 << 0,    /* 1 */
    PERAN_TULIS   = 1 << 1,    /* 2 */
    PERAN_HAPUS   = 1 << 2,    /* 4 */
    PERAN_ADMIN   = PERAN_BACA | PERAN_TULIS | PERAN_HAPUS   /* 7 */
} Peran;

/* Konversi enum ke string deskriptif */
const char *nama_lampu(LampuLaluLintas l) {
    switch (l) {
        case MERAH:  return "Merah";
        case KUNING: return "Kuning";
        case HIJAU:  return "Hijau";
        default:     return "Tidak dikenal";
    }
}

int main(void) {
    LampuLaluLintas lampu = KUNING;
    printf("Lampu saat ini: %s (kode %d)\n", nama_lampu(lampu), lampu);

    /* Bitmask kombinasi peran */
    Peran peran_user = PERAN_BACA | PERAN_TULIS;
    printf("\nPeran user: %d\n", peran_user);
    printf("  bisa baca?  %s\n", (peran_user & PERAN_BACA)  ? "ya" : "tidak");
    printf("  bisa tulis? %s\n", (peran_user & PERAN_TULIS) ? "ya" : "tidak");
    printf("  bisa hapus? %s\n", (peran_user & PERAN_HAPUS) ? "ya" : "tidak");
    printf("  admin?      %s\n", (peran_user == PERAN_ADMIN) ? "ya" : "tidak");

    return 0;
}

Output yang Dihasilkan:

text
Lampu saat ini: Kuning (kode 1)

Peran user: 3
  bisa baca?  ya
  bisa tulis? ya
  bisa hapus? tidak
  admin?      tidak

Pada contoh di atas, typedef enum {...} LampuLaluLintas; membuat alias tipe LampuLaluLintas sehingga kita bisa menulis LampuLaluLintas lampu; tanpa awalan enum. Fungsi nama_lampu memakai switch untuk mengubah nilai enumerator menjadi string deskriptif—pola yang sangat umum agar output program lebih mudah dibaca manusia. Untuk Peran, setiap konstanta diberi nilai pangkat 2 (1, 2, 4) dengan operator shift <<. Operasi bitwise | menggabungkan peran menjadi satu nilai (PERAN_BACA | PERAN_TULIS = 3), dan operasi & mengecek apakah peran tertentu aktif. PERAN_ADMIN didefinisikan sebagai kombinasi ketiga peran lainnya—angkanya 7, dan perbandingan peran_user == PERAN_ADMIN menghasilkan “tidak” karena pengguna hanya punya peran baca dan tulis (3 ≠ 7).