首页 > 编程笔记

C语言联合体(union)的定义、初始化和使用

在C语言中,可以定义不同数据类型的数据共占同一段内存空间,以满足某些特殊的数据处理要求,这种数据构造类型就是联合体。

C语言联合体类型的定义

联合体也是一种构造数据类型,和结构体类型一样,它也是由各种不同类型的数据组成,这些数据叫作联合体的成员。不同的是,在联合体中,C语言编译系统使用了覆盖技术。

联合体的所有成员在内存中具有相同的首地址,共占同一段内存空间,这些成员可以相互覆盖,因此联合体也常常被称作共用体,在不同的时间保存不同的数据类型和不同长度的成员的值。也就是说,在某一时刻,只有最新存储的成员是有效的。运用此种类型数据的优点是节省存储空间。

联合体类型定义的一般形式为:

union 联合体名
{
    数据类型1 成员名1;
    数据类型2 成员名2;
    …
    数据类型n 成员名n;
};

其中,union 是C语言中的关键字,表明进行一个联合体类型的定义。

联合体类型名是一个合法的C语言标识符,联合体类型成员的数据类型可以是C语言中的任何一个基本数据类型,最后的分号表示联合体定义的结束。例如:

union ucode
{
    char u1;
    int u2;
    long u3;
};

这里定义了一个名为 “ucode” 的联合体类型,它包括 3 个成员,分别是字符型、整型和长整型。

说明:联合体类型的定义只是由用户构造了一个联合体,定义好后可以像C中提供的基本数据类型一样被使用,即可以用它来定义变量。但定义联合体类型时,系统并不为其分配存储空间,而是为由该联合体类型定义的变量分配存储空间。

C语言联合体变量的定义

在一个程序中,一旦定义了一个联合体类型,也就可以用这种数据类型定义联合体变量。和定义结构体变量一样,定义联合体类型变量的方法有以下 3 种。

1) 定义联合体类型后定义变量

一般形式如下。

union 联合体名
{
    数据类型1 成员名1;
    数据类型2 成员名2;
    …
    数据类型n 成员名n;
};

union 联合体名 变量名1, 变量名2, … , 变量名n;

2) 定义联合体类型的同时定义变量

一般形式如下。

union 联合体名
{
    数据类型1 成员名1;
    数据类型2 成员名2;
    …
    数据类型n 成员名n;
}   变量名1, 变量名2, … , 变量名n;

说明:在实际应用中,定义联合体的同时定义联合体变量适合于定义局部使用的联合体类型或联合体类型变量,例如在一个文件内部或函数内部。

3) 直接定义联合体类型变量

这种定义方式是不指出具体的联合体类型名,而直接定义联合体成员和联合体类型的变量。一般形式如下。

union
{
    数据类型1 成员名1;
    数据类型2 成员名2;
     …
    数据类型n 成员名n;
}   变量名1, 变量名2, … , 变量名n;


由于此联合体没有标识符,所以无法采用定义联合体变量的第 1 种方法来定义变量。在实际应用中,此方法适合于临时定义局部使用的联合体类型变量。说明如下。

C语言联合体变量的初始化

定义联合体变量的同时对其成员赋初值,就是对联合体变量的初始化。那么,对联合体变量初始化可以和结构体变量一样,在定义时直接对其各个成员赋初值吗?看看下面的这段程序代码。
union ucode
{
   char u1;
   int u2;
};        /*定义联合体类型*/
union ucode a={'a',45};     /*定义联合体类型的变量a并初始化*/
编译时却提示错误信息 “too many initializers”,这是为什么?

这是因为和结构体变量的存储结构不同,联合体变量中的成员是共用一个首地址,共占同一段内存空间,所以在任意时刻只能存放其中一个成员的值。

也就是说,每一瞬时只能有一个成员起作用,所以,在对联合体类型的变量定义并初始化时,只能是对第 1 个成员赋初值,初值需要用 “{}” 括起来。

【示例1】联合体类型的应用举例。代码入下:
#include "stdio.h"
int main()
{
    union
    {
        long u1;
        char u2;
    } a={0x974161};  /*定义联合体类型的变量a*/
    printf("%ld %c\n",a.u1,a.u2);
}
运行结果:

9912673 a

程序中定义的联合体类型包含两个成员 u1 和 u2,分别是 4 个字节的长整型和 1 个字节的字符型,编译系统会按其中占用空间较多的长整型变量给联合体类型的变量 a 分配 4 个字节的存储空间。

赋初值十六进制的 974161 进行初始化后的存储情况如图所示。

输出时,a.u1 输出它得到的初始值 0x974161,以十进制长整型输出 9912673;a.u2 并没有得到初值,但由于它和 a.u1 共用首地址,共用内存,所以在输出时,它取其中低位的一个字节 01100001,并把它以字符“a”的形式输出。

再把【示例1】的程序稍作改动,即把联合体中的第 1 个成员 u1 定义为字符型,把 u2 定义为长整型。

【示例2】联合体类型的应用举例。代码入下:
#include "stdio.h"
int main()
{
    union ucode
    {
        char u1;
        long u2; 
    }a={0x974161};
    printf("%c %ld\n",a.u1,a.u2);
} 
运行结果:

a 97

程序中定义的联合体类型包含两个成员 u1 和 u2,分别是 1 个字节的字符型和 4 个字节的长整型,编译系统会按其中占用空间较多的长整型变量给联合体类型的变量 a 分配 4 个字节的存储空间。

赋初值进行初始化后,由于第 1 个成员是字符型,仅用 1 个字节,所以初值十六进制的 974161 只能接受 0x61,其他高位部分被舍去。存储情况如下图所示。

输出时,a.u1 输出它得到的初始值 0x61,以字符的形式输出 “a”;a.u2 并没有得到初值,但由于它和 a.u1 共用首地址,共用内存,所以在输出时,以十进制长整型输出 97。

请认真比较以上两个示例。

C语言联合体变量的引用

联合体变量不能整体引用,对联合体变量的赋值、使用都只能对变量的成员进行,联合体变量引用其成员的方法与访问结构体变量成员的方法相同。例如:

union ucode
{
    char u1;
    int u2;
    long u3;
};
uion ucode a;

对其中的联合体中成员的引用使用运算符“.”,方法如下:

a.u1, a.u2


【示例3】联合体变量引用的应用举例。代码入下:
#include "stdio.h"
int main()
{
    union ucode   /*定义联合体类型*/
    {
        char u1;
        int u2;
    };
    union ucode a;   /*定义联合体类型的变量*/
    a.u2=5;
    printf("输入a.u1的值:\n");  /*提示信息*/
    scanf("%d",&a.u1);
    printf("输出数据:\n");
    printf("%c\n",a.u1);   /*提示信息*/
    printf("%d\n",a.u2);
}
运行结果:

输入a.u1的值:
65
输出数据:
A
65

本示例旨在巩固联合体变量成员的两种引用方法,并且进一步熟悉联合体类型数据的特点。

优秀文章