天天看點

【C語言】聯合與枚舉類型Motivation一、聯合(union)二、枚舉(enum)3. 參考資料

Motivation

我這人有一個最大的毛病,學什麼東西都總喜歡學個皮毛。

記得大一那會兒我們學C,教材上有幾個内容不考,枚舉和共用體正是其中之一,我就想當然地覺得沒用,也沒看。直到前幾天在看深度學習架構caffe的時候才回過神來,原來任何東西都是有用的啊!

這篇文章就希望能夠對枚舉類型enum和共用體union做一個基本的介紹。

一、聯合(union)

1.1 union是什麼

union,又稱聯合體或是共用體,是一個能在同一塊存儲空間中(但非同時)存儲不同類型資料的資料類型。也就是說幾種成員資料“共用”了同一塊存儲空間。聯合體特别适用于那些彼此互斥,但卻又有一定聯系的内容。聯合體的作用不光是節省存儲空間那麼簡單,更重要的是為資料提供了一個統一的封裝和外部通路的接口。C語言編譯器保證了union的共用體的長度等于最長的成員的長度。

共用體在使用方法上與結構體有些類似,但切記二者是完全不同的!

1.2 union的定義

union的定義形式為:

union 共用體名 {
    類型名    成員名;
    類型名    成員名;
    ... ...
    類型名    成員名n;
};   
           

下面是一個共用體的例子,在這個例子中,我們定義了共用體類型union group,它有3個成員,分别是int, char, double。由于double成員的長度最大,為8 Bytes,是以,共用體的長度也應為8 Bytes。

// Definition of union
union group {
    int    digit;
    double myfloat;
    char   letter;
};
           

下面的配圖或許能夠更好地解釋共用體的含義。

【C語言】聯合與枚舉類型Motivation一、聯合(union)二、枚舉(enum)3. 參考資料

1.2 union的聲明

同結構體類似,union也可以聲明為變量、數組、指針等等,如下所示。

union group valA;         // union group聯合類型的變量
union group valArray[]; // 聯合類型的數組,長度為10
union group * pA;         // 聯合類型的指針
           

第一個聲明建立一個union group類型的變量,編譯器将配置設定足夠的空間用以儲存可能被描述的最大的需要。此處,成員double所占的空間最大,為8 bytes。第二行建立了一個資料valArray,它包含有10個元素。每個元素大小為8 bytes,第三行聲明了一個union group *指針,它可以存放聯合體的位址。

1.3 union的初始化和使用

根據C99标準,聯合體的初始化與結構體不盡相同。具體地,要初始化聯合體,你有三種選擇:

  • 可以将一個聯合初始化為另一個聯合;
  • 可以将一個聯合初始化為其第一個成員;
  • 可以指定聯合的某個成員進行初始化。

    下面的代碼展示了如何進行初始化:

union group valA;         
valA.letter = 'R';
union group valB = valA;  // 将一個聯合初始化為另一個聯合
union group valC = {88};  // 可以将一個聯合初始化為其第一個成員
union group valD = {.myfloat = 118.2};  // 可以指定聯合的某個成員進行初始化
           

下面的代碼則展示了如何使用聯合體:

valA.digit   = ;    // 将7存儲到valA中,使用2 bytes
valA.myfloat = ; // 将7抹去,存儲2.23, 使用8 bytes
valA.letter  = 'T';  // 将2.23抹去,存儲字母T,使用1 bytes
           

點運算符表示正在使用何種資料類型,在同一個時間内隻能存儲一個值。本例中,即使有足夠的空間,也不能存儲int型和char型。儲存何種資料類型有開發者記憶并管理。建議您在寫程式的時候寫寫注釋幫助記憶。

上文中提到了定義指向聯合的指針,事實上,我們同樣可以使用指針對聯合進行操作,如下所示:

pA = & valB;
x  = pA -> digit;
           

最後給出一個關于共用體union的一個綜合的測試例子:

#include <stdio.h>
#include <stdlib.h>

/* Topic: The usage of ``union``
 * Author: LiBin
 * Time: 2015 / 12 / 27
 */

// Definition of union
union group {
    int digit;
    double myfloat;
    char letter;
};

int main() {
    // Declaration of union
    union group valArray[]; // union for array
    union group * pA;         // union for array

    union group valA;
    valA.letter = 'R';
    union group valB = valA;
    union group valC = {};
    union group valD = {.myfloat = };

    // Test the length of union.
    printf("Now, value of `valA` is: %c\n", valA.letter);
    printf("The length of union group is: %d\n", sizeof(union group));

    // Usage of union
    valA.digit = ;
    printf("Now, value of `valA` is: %d\n", valA.digit);

    // Using pointer
    pA = &valA;
    pA->myfloat = ;
    printf("Now, value of `valA` is: %.4f\n", valA.myfloat);

    return ;
}
           

輸出結果為:

Now, value of

valA

is: R

The length of union group is: 8

Now, value of

valA

is: 23

Now, value of

valA

is: 9.8800

二、枚舉(enum)

2.1 enum是什麼

枚舉類型(enumerated type)是一種代表整數常量的資料類型。通過關鍵字enum,可以建立一個新“類型”并指定它的值。枚舉類型的文法與結構體的文法相類似。設計枚舉類型的目的在于提高程式的可讀性。

例如,可以使用枚舉類型指代是或非,使用“1”代表是,使用“0”代表非。又如,可以使用枚舉類型代表一個星期的七天。使用整形1~7代表從Monday到Sunday的七天。

值得注意的是,雖然枚舉常量是int型的,但枚舉常量可以寬松地限定為任何一種整數類型,隻要該類型能儲存這些枚舉常量。例如,上述中星期的取值從1到7,我們就可以使用unsigned char來表示。

2.2 enum的定義

由于枚舉類型比較地簡單,是以直接看下面的例子:

// Define of the enum
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
};
           

有必要對上述代碼進行一定的說明:

  • 枚舉類型的第一個成員預設為0,後續成員的值在前一個成員的基礎上自增1;
  • 可以認為設定枚舉類型的值,進而使枚舉類型在某一個範圍内;
  • 枚舉類型實際上是#define的包裝,因而習慣上在命名時使用大寫。

如上述代碼,我們定義了enum WEEKDAY類型,用以代表一個星期的七天,我們自行設定了枚舉類型的初始值。使得枚舉類型的取值範圍為:1~7。

2.2 enum的聲明

既然枚舉類型也是一種資料類型,那麼它和基本資料類型一樣可以聲明為變量。

一般而言,枚舉類型的聲明分為3種:

  • 枚舉類型的定義與變量聲明分開;
  • 枚舉類型定義的同時進行變量聲明;
  • 用typedef給枚舉類型起别名

由于枚舉類型比較地簡單,這裡直接給出相關代碼:

1.枚舉類型的定義與變量聲明分開:

// Definition
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
};
// Declaration
enum DAY tomorrow; 
enum DAY good_day, bad_day;
           

2.枚舉類型定義的同時進行聲明:

// Definition & Declaration
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
} workday;
           

3.使用typedef給枚舉類型起别名:

// Definition
typedef enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
} weekday;
// Declaration
weekday today;
           

特别說明一點,在第三種情況中,使用typedef給枚舉類型起别名,是可以将關鍵字enum後面的“WEEKDAY”省略掉的。

2.3 enum的初始化和使用

本小結将首先介紹枚舉類型的初始化問題,然後對使用中一些問題進行說明,最後,通過一個綜合執行個體對枚舉類型進行全面的總結。

枚舉類型的初始化同樣可以使用下述3種方式:

  • 先聲明變量,然後再指派;
  • 聲明變量和指派同時進行;
  • 枚舉類型定義、聲明、賦初值同時進行。

1.先聲明變量,再賦初值。

// Definition
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
};
// Declaration
enum DAY tomorrow; 
enum DAY good_day, bad_day;
// Init
tomorrow = WED;
good_day = SAT;
bad_day  = MON;
           

2.聲明和指派同時進行

// Definition
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
};
// Declaration & Init
enum WEEKDAT today = TUE,
         yesterday = MON;
           

3.類型定義、變量聲明、指派同時進行

// Definition & Declaration & Init
enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
} today = TUE, yesterday = MON;
           

這裡需要強調的一點是,盡管方法3看上去比較地簡介,但這裡還是建議大家定義、聲明、指派能夠分開,因為這樣會增加程式的可讀性。将所有内容混在一起在日後debug的時候可能會很頭痛。

下面結合一個具體的例子來讨論枚舉類型中強制類型轉換的問題。前文曾經提到過,枚舉類型實際上是一種指代整形常量的資料類型,那麼自然會考慮到,能否使用數學操作呢?答案當時可以的,但是在操作時,需要對類型進行強制類型轉換,轉換為枚舉類型,才能使用,如下所示:

enum WEEKDAY {
      MON=, TUE, WED, THU, FRI, SAT, SUN
}
enum WEEKDAY today, tomorrow;
today = TUE;
tomorrow = (enum WEEKDAY)(today + );  // 類型轉換
printf("today is %d, tomorrow is %d\n",today, tomorrow);
           

如上例所示,我們将“TUE”也就是整型指派給了today,然後計算today加一後的結果,然而1是int型的,因而我們必須将運算後的結果,顯示地轉換為枚舉類型enum WEEKDAY才能指派給tomorrow。

輸出結果為:

today is 2, tomorrow is 3

最後我們來通過一個綜合執行個體,對枚舉類型做一個總結。

#include<stdio.h>
#include <stdlib.h>

/* Topic: The usage of ``union``
 * Author: LiBin
 * Time: 2015 / 12 / 27
 */

// Definition
typedef enum WEEKDAY {
    MON=, TUE, WED, THU, FRI, SAT, SUN
} weekday;

// Output the day according to the enum WEEKDAY!
void printDay(weekday day);

int main()
{
    // enum WEEKDAY
    printf("The size of the `enum WEEKDAY` is: %d\n", sizeof(enum WEEKDAY) );
    // Declaration
    weekday today;
    weekday tomorrow;
    // Implementation
    today = TUE;
    tomorrow = (enum WEEKDAY)(today + );
    printDay(today);
    printDay(tomorrow);

    return ;
}

void printDay(weekday day) {
    switch(day) {
    case :
        printf("This is Monday.\n");
        break;
    case :
        printf("This is Tuesday.\n");
        break;
    case :
        printf("This is Wednesday.\n");
        break;
    case :
        printf("This is Thursday.\n");
        break;
    case :
        printf("This is Friday\n");
        break;
    case :
        printf("This is Saturday\n");
        break;
    case :
        printf("This is Sunday\n");
        break;
    default:
        printf("Illegal parameter! Please check. quit...\n");
        break;
    }
}
           

執行後的結果應該為:

The size of the

enum WEEKDAY

is: 4

This is Tuesday.

This is Wednesday.

3. 參考資料

[1] Waite, Mitchell, Stephen Prata, and Donald Martin. C primer plus. Sams, 1987.

2015.12.27于浙大

繼續閱讀