C 語言程式設計教學:聯合 (Union)

PUBLISHED ON AUG 21, 2018 — PROGRAMMING

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

    宣告聯合

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

    union sample {
        float f;
        int i;
        char ch;
    };
    
    int main(void) {
        union sample s;
        
        s.i = 3;
        
        return 0;
    }
    

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

    // Forward declaration.
    typedef union sample Sample;
    
    union sample {
        float f;
        int i;
        char ch;
    };
    
    int main(void) {
        Sample s;
        
        s.f = 3.0;
        
        return 0;
    }
    

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

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

    存取聯合中的元素

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

    #include <assert.h>
    
    typedef union sample Sample;
    
    union sample {
        float f;
        int i;
        char ch;
    };
    
    int main(void) {
        Sample 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 Amount;
    
    union amount {
        unsigned unit;
        float liter;
    };
    
    typedef struct item Item;
    
    struct item {
        char *name;
        unsigned short amountType;
        Amount howmuch;
    };
    
    int main(void) {
        Item books = {
            .name = "C Programming Tutorial",
            .amountType = 1,
            .howmuch.unit = 4
        };
        
        Item apples = {
            .name = "Apple",
            .amountType = 1,
            .howmuch.unit = 6
        };
        
        Item juices = {
            .name = "Orange Juice",
            .amountType = 2,
            .howmuch.liter = 3.2
        };
        
        Item 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"); // tailing newline.
        }
        
        return 0;
    }
    

    內嵌在聯合內的結構

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

    #include <stddef.h>
    #include <stdio.h>
    
    typedef struct _rgb RGB;
    
    struct _rgb {
        unsigned short r;
        unsigned short g;
        unsigned short b;
    };
    
    typedef union _color_t {
        char *description;
        RGB rgb;
    } color_t;
    
    typedef struct color Color;
    
    struct color {
        unsigned short type;
        
        color_t data;
    };
    
    int main(void) {
        Color red = {
            .type = 1,
            .data.description = "red"
        };
        
        Color orange = {
            .type = 1,
            .data.description = "orange"
        };
        
        Color beige = {
            .type = 2,
            .data.rgb = { 245, 245, 220 }
        };
        
        Color 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;
    }
    
    comments powered by Disqus