C語言聯合體

像結構體一樣,聯合體(Union)在C語言中是一個用戶定義的數據類型,用於保存不同類型的元素。

但它並不佔所有成員的內存總和。它只佔最大成員的內存,它分享最大成員的內存。

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