C語言聯合體


像結構體一樣,聯合體(Union)在C語言中是一個使用者定義的資料型別,用於儲存不同型別的元素。

但它並不佔所有成員的記憶體總和。它只佔最大成員的記憶體,它分享最大成員的記憶體。

聯合體優點

它佔用較少的記憶體,因為它只佔最大的成員的記憶體量。

聯合體缺點

它將資料儲存在一個成員中。

定義聯合體

union關鍵字用於定義聯合體。下面我們來看看如何在C語言中定義聯合體的語法 -

union union_name   
{  
    data_type member1;  
    data_type member2;  
    .  
    .  
    data_type memeberN;  
};

我們來看看在C語言中定義員工聯合體的例子。如下程式碼 -

union employee  
{   int id;  
    char name[50];  
    float salary;  
};

註:除了union關鍵字,其它均與結構體一樣。

聯合體的例子

我們來看看一個簡單的C語言聯合範例。建立一個原始檔:union-example.c,其程式碼實現如下 -

#include <stdio.h>  
#include <string.h>  
union employee
{
    int id;
    char name[150];
}e1;  //declaring e1 variable for union  

int main()
{
    //store first employee information  
    e1.id = 1010;
    strcpy(e1.name, "Maxsu");//copying string into char array  
                                     //printing first employee information  
    printf("employee 1 id : %d, address: %u\n", e1.id, &e1.id);
    printf("employee 1 name : %s, address: %u\n", e1.name, &e1.name);
    e1.id = 1010;
    printf("employee 1 id : %d, address: %u\n", e1.id, &e1.id);
    printf("employee 1 name : %s, address: %u\n", e1.name, &e1.name);
    return 0;
}

執行上面範例程式碼,得到以下結果 -

employee 1 id : 1937269069, address: 16819328
employee 1 name : Maxsu, address: 16819328
employee 1 id : 1010, address: 16819328
employee 1 name : ?, address: 16819328

如上輸出結果中可以看到,id欄位的值是一個垃圾值,因為name具有大的記憶體大小。所以只有name才具有實際值。

聯合體完全就是共用一個記憶體首地址,並且各種變數名都可以同時使用,操作也是共同生效。如此多的存取記憶體手段,確實好用,不過這些「手段」之間卻沒法互相遮蔽——就好像陣列+下標和指標+偏移一樣。

由於聯合體中的所有成員是共用一段記憶體的,因此每個成員的存放首地址相對於於聯合體變數的基地址的偏移量為0,即所有成員的首地址都是一樣的。為了使得所有成員能夠共用一段記憶體,因此該空間必須足夠容納這些成員中最寬的成員。對於這句「對齊方式要適合其中所有的成員」是指其必須符合所有成員的自身對齊方式。

下面舉例說明:

union U
{
    char s[9];
    int n;
    double d;
};

s占9位元組,n占4位元組,d占8位元組,因此其至少需9位元組的空間。然而其實際大小並不是9,用運算子sizeof測試其大小為16.這是因為這裡存在位元組對齊的問題,9既不能被4整除,也不能被8整除。因此補充位元組到16,這樣就符合所有成員的自身對齊了。從這裡可以看出聯合體所佔的空間不僅取決於最寬成員,還跟所有成員有關係,即其大小必須滿足兩個條件:1)大小足夠容納最寬的成員;2)大小能被其包含的所有基本資料型別的大小所整除。

測試程式,建立一個原始檔:union-test.c,其程式碼實現如下 -

#include <stdio.h>  
#include <string.h>  

union U1
{
    char s[9];
    int n;
    double d;
}u1;

union U2
{
    char s[5];
    int n;
    double d;
}u2;

int main()
{
    printf("%d\n", sizeof(u1));
    printf("%d\n", sizeof(u2));
    printf("0x%x\n", &u1);
    printf("0x%x\n", &u1.s);
    printf("0x%x\n", &u1.n);
    printf("0x%x\n", &u1.d);
    u1.n = 1;
    printf("%d\n", u1.s[0]);
    printf("%lf\n", u1.d);
    unsigned char *p = (unsigned char *)&u1;
    printf("%d\n", *p);
    printf("%d\n", *(p + 1));
    printf("%d\n", *(p + 2));
    printf("%d\n", *(p + 3));
    printf("%d\n", *(p + 4));
    printf("%d\n", *(p + 5));
    printf("%d\n", *(p + 6));
    printf("%d\n", *(p + 7));
    return 0;
}

執行上面測試程式碼,得到以下結果 -

16
8
0xeca5a0
0xeca5a0
0xeca5a0
0xeca5a0
1
0.000000
1
0
0
0
0
0
0
0