Çıplak sayılarla dolu kod, kimsenin okuyamadığı koddur. if (state == 2) size hiçbir şey söylemez; if (state == STATE_RUNNING) her şeyi söyler. Bir enum, C'nin küçük ve sabit bir tam sayı kümesine düzgün isimler verme yoludur - ve bir yığın #define'ın aksine, derleyicinin bildiği tek bir tip altında gruplanmış gelirler.
Bir Enum Tanımlamak
Bildirim bir etiket adlandırır ve numaralandırıcıları süslü parantez içinde listeler:
Hemen dikkat edilecek iki şey. Birincisi, numaralama 0'dan başlar ve birer birer artar, dolayısıyla RED 0, GREEN 1, BLUE 2'dir. İkincisi, numaralandırıcı isimleri çevreleyen kapsamın her yerinde görünürdür - Color.GREEN değil, GREEN yazarsınız. Değişkenlerinizle aynı isim alanında yaşarlar; C kodunun çakışmaları önlemek için geleneksel olarak onları BÜYÜK yazmasının ya da tiple öneklemesinin (COLOR_RED) sebebi budur.
Bir enum'u yazdırmak %d kullanır, çünkü bir numaralandırma değeri bir tam sayı değeridir.
Açık Değerler
Herhangi bir numaralandırıcıya açık bir sabit atayın, ondan sonrakiler oradan devam eder:
Değerlerin benzersiz ya da sıralı olması gerekmez - iki numaralandırıcı bir sayıyı paylaşabilir ki bu takma adlar için zaman zaman yararlıdır (COLOR_DEFAULT = COLOR_BLACK). Derleme zamanı tam sayı sabitleri olmalıdırlar, dolayısıyla birini bir değişkenden hesaplayamazsınız.
Yaygın bir deyim bunu sayıyı kaydetmek için kullanır:
enum Suit {
CLUBS, DIAMONDS, HEARTS, SPADES,
SUIT_COUNT /* otomatik olarak 4 - yeni bir renk eklerseniz doğru kalır */
};
SUIT_COUNT gerçek bir renk değildir; onların sayısıdır ve otomatik numaralama tarafından bedavaya güncel tutulur. Döngüler ve dizi boyutları onu kullanabilir ve asla bayatlamaz.
typedef enum
Yapılarda olduğu gibi, C'de tam tip adı enum Color'dır ve bir typedef anahtar kelimeyi düşürür:
Numaralandırıcılar 0'dan başlayan sıradan tam sayılar olduğu için dizi indeksi görevi de görürler - dx[DIR_EAST] hiçbir dönüşüm olmadan çalışır. Sıfır tabanlı bir enum'un tek en yararlı özelliği budur.
switch İçinde Enum'lar
Bir enum'u bir switch ile eşleştirmek, tipin kendini ödemeye başladığı yerdir. -Wall ile derleyin ve derleyici bir durumu unuttuğunuzda size söyler.
Enum'a bir STATE_FAILED ekleyin, GCC uyarır: enumeration value 'STATE_FAILED' not handled in switch. O uyarı, C'nin bir bütünlük denetimine en yakın şeyidir ve bu switch'leri default: durumu olmadan yazmak için güçlü bir sebeptir - bir default uyarıyı susturur ve yeni durumların ele alınmadan geçmesine izin verir. Bunun yerine geri dönüşü, describe'ın yaptığı gibi switch'ten sonra koyun.
Bir Enum'u Metne Çevirmek
C çalışma zamanında hiç isim tutmaz; printf("%s", GREEN) çalışamaz, çünkü GREEN yalnızca 1'dir. İki kalıp bunu çözer.
Derleyicinin denetlediği switch sürümü:
Ve daha kısa ama denetimsiz olan arama tablosu sürümü - dizi ile enum birbirinden koparsa hiçbir şey uyarmaz:
Belirtilmiş dizi indeksleri ([RED] = "RED") en azından isimleri yuvalarına bağlı tutar, dolayısıyla enum'u yeniden sıralamak metinleri sessizce karıştıramaz. Sınır denetimi önemlidir, çünkü hiçbir şey çağıranın enum dışında bir değer geçirmesini engellemez - bkz. sonraki bölüm.
Enum'lar Su Geçirmez Değildir
Bir enum değişkeni altta bir tam sayıdır ve C aralığı denetlemez:
Numaralandırıcılar üzerinde aritmetik çalışır, düz int'lerle karşılaştırmalar çalışır ve aralık dışı bir dönüşüm kabul edilir. Dolayısıyla bir enum'a mükemmel bir belgeleme ve derleyici için iyi bir ipucu gözüyle bakın, bir garanti olarak değil. Programınızın dışından gelen - bir dosya, bir argüman, bir ağ mesajı - her değeri, üzerinde switch yapmadan önce doğrulayın.
Boyut ve Depolama
C17'de her numaralandırıcı bir int'e sığmalıdır - enum Huge { BIG = 3000000000 };, katı bir derleyicinin (-pedantic-errors) reddettiği bir kısıt ihlalidir; gcc ve clang varsayılan kipte bunu bir uzantı olarak kabul etse de. Numaralandırıcıların kendisi int tipindedir; enum tipinin boyutu ise gerçekleştirime bağlıdır (pratikte int):
(C23 bu kısıtı kaldırır: bir numaralandırıcı int aralığını aşabilir ve enum E : unsigned long { ... } ile alttaki tipi bile seçebilirsiniz. Araç zinciriniz C23'ü hedefleyene kadar numaralandırıcıları int içinde tutun.)
Kesin seçim gerçekleştirime bağlı olduğu için bir enum'u asla doğrudan ikili bir dosyaya ya da ağ paketine yazıp başka bir makinenin onu geri okumasını beklemeyin. Sınırda <stdint.h>'deki uint8_t ya da uint32_t gibi sabit genişlikli bir tipe dönüştürün.
Enum ile #define Karşılaştırması
İkisi de bir sayıya isim verir. Enum genellikle daha iyidir:
enum | #define | |
|---|---|---|
| İşleyen | derleyici | ön işlemci |
| Gruplama | ilişkili değerler bir tipi paylaşır | her sabit bağımsızdır |
| Otomatik numaralama | evet | hayır, elle numaralarsınız |
| Hata ayıklayıcıda görünür | çoğu zaman, isimle | hayır, yalnızca sayı |
switch bütünlük uyarısı | evet | hayır |
| Kapsam | blok kapsamına uyar | metinsel, #undef'e kadar |
Sabit bir tam sayı olmadığında (bir metin, bir float) ya da koşullu derlemede olduğu gibi ön işlemcinin kendisi tarafından kullanılabilir olması gerektiğinde #define'a uzanın. İlişkili tam sayı durumları, bayraklar ya da türlerden oluşan bir küme için bir enum kullanın.
Adlandırma Gelenekleri
Enum'lar kolayca çakışır, çünkü isimleri sıradan kapsamda durur. Büyük C kod tabanlarını aklı başında tutan gelenekler:
- Her numaralandırıcıyı tiple önekleyin:
COLOR_RED,STATE_IDLE,HTTP_OK. Bir grafik programındaki öneksizRED, eninde sonunda birinin başka birRED'iyle karşılaşacaktır. - Numaralandırıcıları BÜYÜK_HARF yazın, diğer sabitlerin göründüğü gibi.
- Tipi tekil PascalCase ya da
snake_case_tolarak adlandırın:Color,Stateya dacolor_t; proje boyunca tutarlı biçimde. - Enum ile döngü kuracak ya da indeksleyecekseniz sona bir
_COUNTnöbetçisi ekleyin.
Sıkça Sorulan Sorular
C'de bir enum nasıl tanımlanır?
enum Color { RED, GREEN, BLUE };, değerleri bu üç adlandırılmış sabit olan bir tip tanımlar. Varsayılan olarak 0'dan numaralanırlar, dolayısıyla RED 0, GREEN 1 ve BLUE 2'dir. Bir değişkeni enum Color c = GREEN; ile bildirin ya da yalnızca Color c yazabilmek için bir typedef ekleyin.
C'de bir enum'a belirli değerler atanabilir mi?
Evet: enum Status { OK = 200, NOT_FOUND = 404, ERROR = 500 };. Bazılarını ayarlayıp diğerlerinin devam etmesine izin verebilirsiniz - açık değeri olmayan her isim bir öncekinden devam eder, dolayısıyla enum { A = 10, B, C }; B'yi 11, C'yi 12 yapar.
C'de bir enum metne nasıl çevrilir?
C'nin yerleşik bir yolu yoktur; numaralandırıcı isimleri çalışma zamanında var olmaz. Alışıldık yaklaşım, değer başına bir metin sabiti döndüren switch içeren küçük bir fonksiyon ya da enum ile indekslenen bir metin dizisidir. Switch sürümü daha güvenlidir, çünkü yeni bir numaralandırıcının case'i yoksa -Wall uyarır.
C'de bir enum'un boyutu nedir?
Gerçekleştirime bağlıdır, ama pratikte sizeof(int) - çoğu sistemde 4 bayt. Derleyici, her numaralandırıcıyı tutabilecek bir tam sayı tipi seçer. İkili bir dosya ya da ağ paketi düzenlerken belirli bir boyut varsaymayın; orada <stdint.h>'den sabit genişlikli bir tip kullanın.