枚舉(enum)是C語言的一種特殊類型。當我們在程式設計中遇到定義一些固定長度或範圍的數值時,可以考慮使用枚舉類型。使用枚舉可以讓我們的程式可讀性更強、看起來更加直覺。舉個例子,如果我們在程式設計中需要使用數字0~6分别表示星期日~星期六,程式的可讀性就不高,我們需要翻手冊或者看程式注釋才能知道每個數字具體代表什麼意思。如果我們使用枚舉呢,基本上不需要看注釋或手冊就可知曉其大意。
enum week // enum 枚舉類型{枚舉值清單};{ SUN,MON,TUE,WED,THU,FRI,SAT,};enum week today = SUN; //定義一個枚舉變量
使用enum定義的枚舉值清單中,預設值是從0開始,然後依次遞增:SUN=0,MON=1...。當然我們也可以顯式指定枚舉值:
enum week{ SUN = 1,MON,TUE,WED,THU = 7,FRI,SAT,};//SUN=1,那麼接下來MON=2,TUE=3,WED=4//THU=7,那麼接下來FRI=8,SAT=9
1. enum經常使用的三種方法
使用枚舉類型定義變量,使用方法跟結構體、共用體類似,經常使用的三種方法如下:
enum week //定義枚舉類型的同時,定義枚舉變量{ SUN,MON,TUE,WED,THU,FRI,SAT,}today, tomorrow;enum //可以省去枚舉類型名,直接定義變量{ SUN,MON,TUE,WED,THU,FRI,SAT,}today, tomorrow;enum week //先定義枚舉類型,再定義枚舉變量{ SUN,MON,TUE,WED,THU,FRI,SAT,};enum week today, tomorrow;
2. 枚舉的本質
在C語言中,枚舉是一種類型,屬于整型的範疇,使用enum定義的枚舉值清單,其實就是從0開始的一系列整數序列。整型除了short、int、long、long long外,還包括char、_Bool(C99标準新增)和enum。是以,枚舉的使用其實和整數值其實沒啥差別:我們使用枚舉類型定義的變量,同樣可以作為函數參數、函數傳回值、用來定義數組、甚至和結構體混用等。
enum week get_week_time (void);int set_week_time (enum week time_set);int change_week_time (enum week *p);enum week a[10];struct student{ char name[20]; int age; enum week birthday;};
枚舉有點類似于typedef,給一個數值添加一個别名,讓我們的程式更加直覺、可讀性更高。枚舉類型在本質上就是有命名的整數,屬于整型的一種,在代碼中是可以和整型互換的。
enum week t = SUN;int t2 = SUN;enum week t3 = t2;enum week t4 = 100;
在上面的代碼中,枚舉變量和整型變量互相指派,都是可以正常編譯和運作的。我們在代碼中使用枚舉類型,在最終編譯生成的可執行檔案中都會被整型數值代替。
enum week{ SUN = 5,MON,TUE,WED,THU,FRI,SAT,};int main (void){ enum week today = THU; return 0;}
在上面的示例代碼中,我們定義了一個枚舉類型week,然後定義了一個枚舉變量today,并指派為THU。反彙編上面的代碼,我們可以看到彙編代碼:
00010400 : 10400: e52db004 push {fp} 10404: e28db000 add fp, sp, #0 10408: e24dd00c sub sp, sp, #12 1040c: e3a03009 mov r3, #9 10410: e50b3008 str r3, [fp, #-8] 10414: e3a03000 mov r3, #0 10418: e1a00003 mov r0, r3 1041c: e24bd000 sub sp, fp, #0 10420: e49db004 pop {fp}; 10424: e12fff1e bx lr
在C程式中定義的枚舉變量today,在彙編代碼的第1040c處,我們可以看到:枚舉值THU被替換為整型數值9。使用枚舉的唯一好處就是增加代碼的可讀性,它的作用跟宏定義的作用有異曲同工之妙。
3. 枚舉和宏
枚舉與預處理指令#define的作用差不多,都是為了增加代碼的可讀性。但在實際使用中,兩者還是有些差别的:宏在預處理階段,通過簡單的字元串替換就全部被替換掉了,編譯器根本不知道有宏這麼一個玩意;而枚舉類型則在編譯階段全部替換為整型。
跟宏相比,枚舉的優勢是:枚舉可以自動指派,而宏則需要一個一個單獨定義。是以,在自定義一些有規則的類型值的時候,使用枚舉會更加友善。枚舉可以自定義的變量值來代替數字值,使我們的程式代碼有更高的可讀性。
4. Linux核心中的枚舉類型
在Linux核心代碼中,充斥着大量的枚舉類型資料,有些枚舉類型的定義看起來很奇怪,比如:
enum{ MM_FILEPAGES, MM_ANONPAGES, MM_SWAPENTS, NR_MM_COUNTERS};enum pid_type{ PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX};
Linux核心中使用enum定義的枚舉類型大部分是沒有枚舉名的,而且通常會在一串枚舉值之後帶上一個NR*的元素用來表示枚舉值的數量。當我們不需要使用枚舉類型去定義一個枚舉變量時,枚舉并不需要一個名字,這些無名的枚舉類型其實就相當于宏定義。而最後一個元素NR或MAX,一般可以用來記載枚舉清單中元素的個數,或者作為循環判斷的邊界值。
5. 使用枚舉需要注意的地方
什麼是類型?類型是一定範圍的數值及方法的集合。枚舉作為整型類型的一種,在程式設計使用過程中,也有一些注意的地方,比如作用域。使用枚舉定義的常量也遵循資料作用域規:包括檔案作用域、代碼塊作用域等,在同一個作用域能不能出現重名的枚舉常量名。
enum week1{ SUN,MON,TUE,WED,THU,FRI,SAT,};enum week2{ SAT,UNKNOW,};int main (void){ return 0;}
在上面的代碼中,我們定義了兩個枚舉類型,其中枚舉常量SAT重名,編譯時就會發生如下錯誤:
error: redeclaration of enumerator `SAT'error: previous definition of 'SAT' was here
出現錯誤的原因是,我們定義的不同枚舉類型中的兩個枚舉常量名同在一個作用域:檔案作用域,我們稍微改一下代碼就可以避免沖突:
#include enum week1{ SUN,MON,TUE,WED,THU,FRI,SAT,
};
int main (void){ printf("%d\n", SAT); enum week2 { SAT,UNKNOW, }; printf("%d\n", SAT); return 0;}
我們将枚舉類型week2的定義放到了main函數内,week2的作用域就從檔案作用域變為代碼塊作用域。這個時候,兩個枚舉類型中的同名枚舉常量就不會再發生沖突,程式的運作結果為:
60
專注嵌入式、Linux精品教程
請關注微信公衆号:宅學部落