*9.5 共用体类型

 

9.5 共用体类型

9.5.1 什么是共用体类型

有时,我们希望用同一段内存单元存放不同类型的变量。例如,将一个短整型变量、一个字符型变量和一个实型变量放在同一个内存地址开始的单元中。如下图所示:

图 9.17 显示了以上三个变量在内存中占用的字节数虽然不同,但都从同一地址开始存放。使用这种覆盖技术时,后一个数据会覆盖前面的数据。这种使几个不同的变量共享同一段内存的结构,被称为“共用体”类型的结构。

定义共用体类型变量

定义共用体类型变量的一般形式为:

 

例如:

union 共用体名 {
    成员表列
} 变量表列;

上面的定义表示不同类型的变量 i, ch, f 可以存放到同一个存储单元中。在声明类型的同时也定义了变量 a, b, c

我们也可以将类型声明与变量定义分开:

union Data {
    int i;
    char ch;
    float f;
} a, b, c;

这表示先声明一个 union Data 类型,再将 a, b, c 定义为 union Data 类型的变量。当然,也可以直接定义共用体变量,例如:

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

union Data a, b, c;

可以看到,“共用体”与“结构体”的定义形式相似,但它们的含义是不同的。结构体变量所占内存长度是各成员占用内存长度之和,每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。例如,上面定义的共用体变量 a, b, c 各占 4 个字节(因为一个 float 型变量占 4 个字节),而不是各占 4+1+4=9 个字节。

国内有些C语言书籍将 union 直译为“联合”。我认为译为“共用体”更能反映这种结构的特点,即几个变量共用一个内存区。而“联合”这个名词在一般意义上容易被理解为“将两个或若干个变量联结在一起”,难以表达这种结构的特点。然而,读者应当知道“共用体”在一些书中也被称为“联合”。在阅读其他书籍时如遇“联合”一词,应理解为“共用体”。

自己的理解

共用体的使用场景在于内存资源有限的情况下,多个变量不会同时被使用时。通过共用体,可以节省内存,尤其是在嵌入式系统或者需要严格控制内存使用的应用中。需要注意的是,使用共用体时要非常小心,因为如果不注意覆盖的顺序和类型,会导致难以调试的错误。此外,现代编译器和调试工具对于共用体的支持也越来越好,可以通过一些工具检测到共用体的使用错误。总的来说,共用体是C语言中一个强大的工具,但使用时需要慎重。

9.5.2 引用共用体变量的方式

只有先定义了共用体变量才能引用它,但应注意,不能引用共用体变量,而只能引用共用体变量中的成员。例如,前面定义了 a, b, c 为共用体变量,下面的引用方式是正确的:

union {
    int i;
    char ch;
    float f;
} a, b, c;

不能只引用共用体变量,例如下面的引用是错误的:

a.i    // 引用共用体变量中的整型变量 i
a.ch   // 引用共用体变量中的字符变量 ch
a.f    // 引用共用体变量中的实型变量 f

因为 a 的存储区可以按不同的类型存放数据,有不同的长度,仅写共用体变量名 a,系统无法知道究竟应输出哪一个成员的值。应该写成:

printf("%d", a);

printf("%c", a.ch);

自己的理解

在使用共用体时,引用成员的方式至关重要。共用体变量中不同类型的成员共享相同的内存空间,因此在引用时必须明确指定具体的成员,否则会引起未定义的行为。尤其在进行输出操作时,直接引用共用体变量名是错误的,因为编译器无法确定要输出的具体类型和内容。因此,必须指定要引用的具体成员,这样才能确保程序的正确性和安全性。这也要求程序员在使用共用体时具备清晰的逻辑和严谨的代码习惯,以避免出现难以排查的错误。

9.5.3 共用体类型数据的特点

在使用共用体类型数据时要注意以下一些特点:

  1. 单一存储:同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个。这是因为在每一个瞬时,存储单元只能有唯一的内容。在共用体变量中只能存放一个值。例如:

    union Data {
        int i;
        char ch;
        float f;
    } a;
    
    a.i = 97;
    printf("%d", a.i);    // 输出整数97
    printf("%c", a.ch);   // 输出字符'a'
    printf("%f", a.f);    // 输出实数0.000000
    

    执行以上代码时,由于97是赋给 a.i 的,因此按整数形式存储在变量单元中。如果用 %d 格式符输出 a.i,会输出整数97;用 %c 格式符输出 a.ch,系统会把存储单元中的信息按字符输出'a';用 %f 格式符输出 a.f,系统会将存储单元中的信息按浮点数形式处理,输出0.000000。

  2. 初始化限制:可以对共用体变量初始化,但初始化表中只能有一个常量。例如:

    union Data {
        int i;
        char ch;
        float f;
    } a = {1};   // 正确,对第一个成员初始化
    

  3. 最后赋值生效:共用体变量中起作用的成员是最后一次被赋值的成员。例如:

    a.ch = 'a';
    a.f = 1.5;
    a.i = 40;
    printf("%d", a.i);    // 输出40
    printf("%c", a.ch);   // 输出'(',因为40的ASCII码对应'('
    

  4. 同一地址:共用体变量的地址和它的各成员的地址都是同一地址。例如,&a.i, &a.ch, &a.f 都是同一个地址。

  5. 不能直接赋值:不能对共用体变量名赋值,也不能引用变量名来得到一个值。例如:

    // a = 1;   // 错误
    int m = a; // 错误
    

  6. C99新特性:C99允许同类型的共用体变量互相赋值,也允许把共用体变量作为函数参数。例如:

    union Data b;
    b = a;   // 合法
    

  7. 结构体与共用体嵌套:共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。

实例分析

在某些情况下,我们需要用同一个内存区存储不同的数据内容。例如,处理学生和教师的数据时,学生的数据包括:姓名、号码、性别、职业、班级;教师的数据包括:姓名、号码、性别、职业、职务。可以用共用体来处理这两种不同的数据类型,使程序更加灵活。

代码示例

#include <stdio.h>

struct {
    int num;
    char name[10];
    char sex;
    char job;
    union {
        int class;
        char position[10];
    } category;
} person[2];

int main() {
    int i;
    for (i = 0; i < 2; i++) {
        printf("Please enter the data of person:\n");
        scanf("%d %s %c %c", &person[i].num, person[i].name, &person[i].sex, &person[i].job);
        if (person[i].job == 's')
            scanf("%d", &person[i].category.class);
        else if (person[i].job == 't')
            scanf("%s", person[i].category.position);
        else
            printf("Input error!\n");
    }
    
    printf("No.\tName\tSex\tJob\tClass/Position\n");
    for (i = 0; i < 2; i++) {
        if (person[i].job == 's')
            printf("%d\t%s\t%c\t%c\t%d\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.class);
        else
            printf("%d\t%s\t%c\t%c\t%s\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.position);
    }
    
    return 0;
}

程序分析: 在 main 函数之前定义了结构体数组 person,在结构体类型声明中包括了共用体类型 category,这个共用体成员中又包括两个成员:classposition。根据职业的不同,输入对应的班级号或职位,并将其存储在共用体中。

通过这种方式,在处理数据时能够灵活地利用同一段内存存储不同类型的数据,从而提高了程序的灵活性和效率。

 

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值