位元詩人 [C 語言] 程式設計教學:如何使用聯合 (Union)

C 語言聯合
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

聯合 (union) 乍看和結構 (structure) 有點像,但聯合內的屬性共用同一塊記憶體,故同一時間內僅能用聯合內其中一種屬性。聯合主要用來表示同概念但不同資料類型的實體。

宣告聯合

使用 union 保留字可宣告聯合,如下例:

union sample_t {
    float f;
    int i;
    char ch;
};

int main(void)
{
    union sample_t s;

    s.i = 3;

    return 0;
}

我們可以用 typedef 來簡化聯合的型別名稱:

/* Forward declaration. */
typedef union sample_t sample_t;

union sample_t {
    float f;
    int i;
    char ch;
};

int main(void)
{
    sample_t s;

    s.f = 3.0;

    return 0;
}

如果想節省命名空間的符號量,可改用以下方法來宣告:

typedef union {
    float f;
    int i;
    char ch;
} sample_t;

這時候的聯合是匿名聯合 (anonymous union),故不占用命名空間。

存取聯合中的元素

我們先前提過,聯合在同一時間同僅能儲存其中一個屬性,故以下程式會引發錯誤:

#include <assert.h>

typedef union sample_t sample_t;

union sample_t {
    float f;
    int i;
    char ch;
};

int main(void)
{
    sample_t s;

    s.i = 3;

    assert(s.i == 3);

    // Update s.
    s.f = 5.0;

    assert(s.f == 5.0);

    // Error!
    assert(s.i == 3);

    return 0;
}

內嵌在結構內的聯合

聯合和結構相似,都是一種複合型別,我們可以在結構內嵌入聯合,這時候的好處在於我們可以用一個額外的欄位來記錄目前聯合中使用的型別,如以下實例:

#include <stddef.h>
#include <stdio.h>

typedef union amount_t amount_t;

union amount_t {
    unsigned unit;
    float liter;
};

typedef struct item_t item_t;

struct item_t {
    char *name;
    unsigned short amountType;
    amount_t howmuch;
};

int main(void)
{
    item_t books = {
        .name = "C Programming Tutorial",
        .amountType = 1,
        .howmuch.unit = 4
    };

    item_t apples = {
        .name = "Apple",
        .amountType = 1,
        .howmuch.unit = 6
    };

    item_t juices = {
        .name = "Orange Juice",
        .amountType = 2,
        .howmuch.liter = 3.2
    };

    item_t items[] = {books, apples, juices};

    for (size_t i = 0; i < 3; i++) {
        printf("%s: ", items[i].name);

        if (items[i].amountType == 1) {
            printf("%d units", items[i].howmuch.unit);
        } else {
            printf("%.2f liters", items[i].howmuch.liter);
        }

        printf("\n"); /* trailing newline. */
    }

    return 0;
}

內嵌在聯合內的結構

除了聯合可嵌在結構內,結構也可以嵌在聯合內。不過,我們為了記錄聯合所用的型別,外部會再用一層結構包住該聯合,就會形成三層的複合型別,如下例:

#include <stddef.h>
#include <stdio.h>

typedef struct rgb_t rgb_t;

struct rgb_t {
    unsigned short r;
    unsigned short g;
    unsigned short b;
};

typedef union color_data_t color_data_t;

union color_data_t {
    char *description;
    rgb_t rgb;
};

typedef struct color_t color_t;

struct color_t {
    unsigned short type;    
    color_data_t data;
};

int main(void)
{
    color_t red = {
        .type = 1,
        .data.description = "red"
    };

    color_t orange = {
        .type = 1,
        .data.description = "orange"
    };

    color_t beige = {
        .type = 2,
        .data.rgb = { 245, 245, 220 }
    };

    color_t colors[] = { red, orange, beige };

    for (size_t i = 0; i < 3; i++) {
        if (colors[i].type == 1) {
            printf("%s\n", colors[i].data.description);
        } else {
            printf("(%u, %u, %u)\n", 
                colors[i].data.rgb.r,
                colors[i].data.rgb.g,
                colors[i].data.rgb.b);
        }
    }

    return 0;
}
關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。