Kode yang penuh angka tanpa nama adalah kode yang tidak bisa dibaca siapa pun. if (state == 2) tidak memberitahumu apa-apa; if (state == STATE_RUNNING) memberitahumu segalanya. enum adalah cara C memberi nama yang layak pada sekumpulan kecil integer yang tetap, dan tidak seperti tumpukan #define, nama-nama itu dikelompokkan dalam satu tipe yang dikenal compiler.
Mendefinisikan Enum
Deklarasinya menyebutkan sebuah tag dan mendaftarkan enumerator di dalam kurung kurawal:
Ada dua hal yang perlu langsung diperhatikan. Pertama, penomorannya dimulai dari 0 dan bertambah satu, jadi RED bernilai 0, GREEN bernilai 1, BLUE bernilai 2. Kedua, nama enumerator terlihat di seluruh scope yang membungkusnya: kamu menulis GREEN, bukan Color.GREEN. Nama-nama itu berada di namespace yang sama dengan variabelmu, karena itulah kode C biasanya menulisnya dengan HURUF BESAR atau memberinya awalan sesuai tipe (COLOR_RED) untuk menghindari bentrokan.
Mencetak enum memakai %d, karena nilai enumerasi adalah nilai integer.
Nilai Eksplisit
Beri enumerator mana pun konstanta eksplisit, dan enumerator sesudahnya akan melanjutkan dari situ:
Nilainya tidak harus unik atau berurutan: dua enumerator boleh memakai angka yang sama, yang sesekali berguna untuk alias (COLOR_DEFAULT = COLOR_BLACK). Nilainya harus berupa konstanta integer saat kompilasi, jadi kamu tidak bisa menghitungnya dari sebuah variabel.
Pola umum memakai hal ini untuk mencatat jumlahnya:
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES,
SUIT_COUNT /* automatically 4 - stays correct if you add a suit */
};
SUIT_COUNT bukan suit sungguhan; itu jumlah suit, yang dijaga gratis oleh penomoran otomatis. Loop dan ukuran array bisa memakainya dan tidak akan pernah ketinggalan zaman.
typedef enum
Seperti struct, nama tipe lengkap di C adalah enum Color, dan typedef menghilangkan kata kuncinya:
Karena enumerator adalah integer biasa yang dimulai dari 0, enumerator sekaligus bisa dipakai sebagai indeks array: dx[DIR_EAST] berfungsi tanpa konversi apa pun. Itulah satu-satunya sifat paling berguna dari enum yang dimulai dari nol.
Enum di switch
Memasangkan enum dengan switch adalah saat tipe ini mulai menunjukkan manfaatnya. Kompilasi dengan -Wall dan compiler akan memberitahumu kalau kamu melupakan sebuah case.
Tambahkan STATE_FAILED ke enum dan GCC memberi peringatan: enumeration value 'STATE_FAILED' not handled in switch. Peringatan itu adalah hal yang paling mendekati pemeriksaan kelengkapan di C, dan menjadi alasan kuat untuk menulis switch seperti ini tanpa case default:: default membungkam peringatannya dan membiarkan state baru lolos tanpa ditangani. Taruh fallback-nya setelah switch, seperti yang dilakukan describe.
Mengonversi Enum Menjadi String
C tidak menyimpan nama apa pun saat program berjalan; printf("%s", GREEN) tidak bisa berfungsi karena GREEN hanyalah 1. Ada dua pola untuk mengatasinya.
Versi switch, yang diperiksa compiler:
Dan versi tabel pencarian, lebih pendek tetapi tidak diperiksa: kalau array dan enum-nya tidak lagi cocok, tidak ada yang memberi peringatan:
Indeks array dengan designated initializer ([RED] = "RED") setidaknya menjaga nama tetap terikat ke slotnya, jadi mengubah urutan enum tidak bisa diam-diam mengacak string-nya. Pemeriksaan batas itu penting karena tidak ada yang mencegah pemanggil memberikan nilai di luar enum; lihat bagian berikutnya.
Enum Tidak Kedap Sepenuhnya
Variabel enum pada dasarnya adalah integer, dan C tidak mengawasi rentangnya:
Aritmetika pada enumerator berfungsi, perbandingan dengan int biasa berfungsi, dan cast yang di luar rentang pun diterima. Jadi anggap enum sebagai dokumentasi yang sangat baik dan petunjuk yang bagus untuk compiler, bukan sebagai jaminan. Validasi setiap nilai yang datang dari luar programmu, entah dari file, argumen, atau pesan jaringan, sebelum memakainya di switch.
Ukuran dan Penyimpanan
Di C17, setiap enumerator harus muat di int: enum Huge { BIG = 3000000000 }; adalah pelanggaran batasan yang ditolak compiler yang ketat (-pedantic-errors), meski gcc dan clang menerimanya sebagai ekstensi di mode default-nya. Enumerator itu sendiri bertipe int, sedangkan ukuran tipe enum ditentukan oleh implementasi (dalam praktiknya, int):
(C23 menghapus batasan itu: enumerator boleh melebihi rentang int dan kamu bahkan bisa memilih tipe dasarnya dengan enum E : unsigned long { ... }. Sampai toolchain-mu menargetkan C23, jaga enumerator tetap di dalam rentang int.)
Karena pilihan pastinya ditentukan oleh implementasi, jangan pernah menulis enum langsung ke file biner atau paket jaringan lalu berharap mesin lain bisa membacanya kembali. Konversikan ke tipe dengan lebar pasti seperti uint8_t atau uint32_t dari <stdint.h> di titik batasnya.
Enum vs #define
Keduanya memberi nama pada sebuah angka. Enum umumnya lebih baik:
enum | #define | |
|---|---|---|
| Ditangani oleh | compiler | preprocessor |
| Pengelompokan | nilai yang saling berkaitan berbagi satu tipe | setiap konstanta berdiri sendiri |
| Penomoran otomatis | ya | tidak, kamu menomori sendiri |
| Terlihat di debugger | sering, berdasarkan nama | tidak, hanya angkanya |
Peringatan kelengkapan switch | ya | tidak |
| Scope | mengikuti block scope | tekstual, sampai #undef |
Gunakan #define kalau konstantanya bukan integer (string, float) atau kalau harus bisa dipakai oleh preprocessor itu sendiri, seperti di kompilasi bersyarat. Untuk sekumpulan state, flag, atau jenis integer yang saling berkaitan, gunakan enum.
Konvensi Penamaan
Enum mudah bertabrakan karena namanya berada di scope biasa. Konvensi yang menjaga codebase C yang besar tetap waras:
- Beri awalan tipe pada setiap enumerator:
COLOR_RED,STATE_IDLE,HTTP_OK.REDtanpa awalan di program grafis suatu saat akan bertemuREDmilik orang lain. - Tulis enumerator dengan SHOUT_CASE, sama seperti tampilan konstanta lainnya.
- Beri nama tipe dalam PascalCase tunggal atau
snake_case_t:Color,State, ataucolor_t, secara konsisten di seluruh proyek. - Tambahkan sentinel
_COUNTdi akhir kalau kamu akan melakukan loop atau akses indeks dengan enum itu.
Pertanyaan yang sering diajukan
Bagaimana cara mendefinisikan enum di C?
enum Color { RED, GREEN, BLUE }; mendefinisikan sebuah tipe yang nilainya adalah tiga konstanta bernama itu. Secara default penomorannya dimulai dari 0, jadi RED bernilai 0, GREEN bernilai 1, dan BLUE bernilai 2. Deklarasikan variabel dengan enum Color c = GREEN;, atau tambahkan typedef supaya kamu cukup menulis Color c.
Apakah enum di C bisa diberi nilai tertentu?
Bisa: enum Status { OK = 200, NOT_FOUND = 404, ERROR = 500 };. Kamu bisa mengisi sebagian dan membiarkan sisanya mengikuti: nama apa pun tanpa nilai eksplisit melanjutkan dari nama sebelumnya, jadi enum { A = 10, B, C }; menghasilkan B = 11 dan C = 12.
Bagaimana cara mengonversi enum menjadi string di C?
C tidak punya cara bawaan; nama enumerator tidak ada saat program berjalan. Pendekatan yang biasa dipakai adalah fungsi kecil dengan switch yang mengembalikan string literal untuk setiap nilai, atau array string yang diakses dengan indeks enum. Versi switch lebih aman karena -Wall memberi peringatan kalau enumerator baru tidak punya case.
Berapa ukuran enum di C?
Ditentukan oleh implementasi, tetapi dalam praktiknya sizeof(int), yaitu 4 byte di sebagian besar sistem. Compiler memilih tipe integer yang bisa menampung setiap enumerator. Jangan menganggap ukurannya pasti saat menyusun file biner atau paket jaringan; di sana, gunakan tipe dengan lebar pasti dari <stdint.h>.