C语言联合(union)原理与实战案例详解

小石头
小石头
发布时间:2026-10-16| 6人浏览过
原创

联合体(union)是C语言中一种特殊的用户自定义数据类型,其所有成员共享同一块内存空间。本文将从原理出发,结合多个具体的代码案例,深入解析联合体的运行机制、内存分配特点以及在复杂数据结构中的应用方法。

核心概念:union与struct的本质差异

在C语言中,结构体(struct)和联合体(union)在语法声明上非常相似,但在内存管理和使用场景上有本质区别:

  • 结构体 (struct):共存模式。各个成员拥有独立的内存地址,总大小是各成员大小之和(考虑对齐填充)。优点是数据全面,缺点是内存占用固定且较大,“有容乃大”。
  • 联合体 (union):互斥模式。所有成员引用内存中的相同起始位置,整体大小等于最大成员的大小。优点是内存使用精细灵活,节省空间;缺点是同一时刻只能安全地存储和使用一个成员的值,其他成员会面临数据覆盖风险。

当需要在不同时刻将不同类型的变量存储于同一内存位置时,联合体是最佳选择。

案例1:基本成员的覆盖机制分析

以下代码展示了当对联合体的不同成员赋值时,底层内存是如何被覆盖的。我们定义了一个包含 int、float 和 char 的联合体。

#include "stdio.h"

union {
    int a;
    float b;
    char c;
} x;

int main() 
{ 
    x.c = 'x';
    x.b = 3.14;
    x.a = 25;
    printf("%g \t%c \t %d\t\n", x.b, x.c, x.a);
    return 0;          
}

执行结果:

3.50325e-44    垃圾值   25

结果解读:

  1. x.c='x':初始化内存。
  2. x.b=3.14:浮点数写入覆盖了之前的字符数据。
  3. x.a=25:整数写入又覆盖了浮点数数据。

由于最后写入的是 x.a,因此打印 x.a 得到正确值 25。而读取 x.b 和 x.c 时,它们对应的内存内容实际上是整数 25 的二进制表示,这解释了为何输出为乱码或极小数值。这验证了联合体成员之间“后写者生存”的互斥特性。

案例2:同类型不同尺寸变量的共享

当联合体成员为相同基本类型但不同尺寸时,它们的内存地址完全一致。观察以下 long int 和 int 的案例:

#include<stdio.h>  
union var {  
        long int l;  
        int i;  
};  

main(){  
        union var v;  
        v.l = 5;  
        printf("v.l is %d\n", v.i);  // 此时 i 的高位可能为0,低几位为5
        
        v.i = 6;  
        printf("now v.l is %ld! the address is %p\n", v.l, &v.l);  
        printf("now v.i is %d! the address is %p\n", v.i, &v.i);  
}

执行结果:

v.l is 5
now v.l is 6! the address is 0xbfad1e2c
now v.i is 6! the address is 0xbfad1e2c

关键点:

  • v.l 和 v.i 的地址指针完全相同(如示例中的 0xbfad1e2c)。
  • 修改任意一个变量都会立即反映在另一个变量上。
  • 注意:虽然在某些小端序系统中,将 5 赋值给 long 后读取 int 也能得到 5,但这依赖于具体平台的字节排列和整数长度,属于未定义行为边缘试探,生产环境中应避免这种跨类型间接访问。

案例3:字节级访问与混合数据解析

联合体最强大的功能之一是允许程序以不同的精度查看同一块内存。以下代码展示了如何通过字符指针拆解联合体内的数据,并利用结束符打印部分字符串。

#include<stdio.h>  
union u {  
    int i;           // 通常占4字节
    double d;        // 占8字节,union大小为8
};  

main(){  
    union u uu;  
    uu.i = 10;  
    printf("%d\n", uu.i);  // 输出 10
    
    // 获取联合体首地址并强转为字符指针
    char * c = (char *)&uu;  
    c[0] = 'a';  
    c[1] = 'b';  
    c[2] = 'c';  
    c[3] = '\0';  // 字符串结束符
    c[4] = 'd';  
    c[5] = 'e';  
    
    printf("%s\n", c);  // 利用 '\0' 截断,仅打印 "abc"
    printf("%c %c %c %c %c %c\n", c[0], c[1], c[2], c[3], c[4], c[5]);
}

执行结果:

10
abc
a b c d e

操作分析:

通过将联合体地址强制转换为 char* 指针,我们可以逐字节地操作内存。代码中在前三个字节分别填入 'a', 'b', 'c',并在第四个字节插入 '\0'。当使用 %s 格式化输出时,遇到 \0 即停止,因此只看到了 abc,而后续的 d 和 e 虽然在内存中,但未随字符串一起打印。

案例4:结构体嵌套联合体处理异构数据

在实际开发(如学生管理系统)中,往往需要存储不同类型的数据。结构体用于容纳常规信息,内部嵌套联合体来处理“互斥”的业务字段(例如:如果是学生输入班级号,如果是员工输入办公室号)。

main()
{
   struct body_type {
      char name[10];
      int age;
      char job;
      union {
         int class_num;
         char office[10];
      } depa; // 嵌套的联合体
   } body[2];
   
   int n, i;
   for(i = 0; i < 2; i++)
   {
      printf("input name,age,job and department\n");
      scanf("%s %d %c", body[i].name, &body[i].age, &body[i].job);
      
      // 根据作业类型决定录入哪种部门信息
      if(body[i].job == 's') // 's' 代表 student
         scanf("%d", &body[i].depa.class_num);
      else                   // 'e' 代表 employee
         scanf("%s", body[i].depa.office);
   }
   
   printf("name\tage\tjob\tclass/office\n");
   for(i = 0; i < 2; i++)
   {
      if(body[i].job == 's')
         printf("%s\t%3d %3c %d\n", body[i].name, body[i].age, body[i].job, body[i].depa.class_num);
      else
         printf("%s\t%3d %3c %s\n", body[i].name, body[i].age, body[i].job, body[i].depa.office);
   }
}

设计逻辑:

  • 使用 if...else 判断 job 字段来决定用户输入流向联合体的哪个成员。
  • 在输出时也保持相同的判断逻辑,确保取出的数据与存入的类型匹配。
  • 这种方式极大地节省了内存,因为对于单个记录,不需要同时开辟 int 和 char[10] 的空间,只需两者中较大的那一个即可。

总结

合理使用联合体可以实现内存的高效复用,特别适合处理协议解析、硬件寄存器映射或多态数据存储等场景。但使用时必须牢记:联合体在任何时刻只能保存一个有效成员的值。程序员需自行维护状态标记(如上述案例中的 job 字段),以确保从正确的成员读取数据,避免产生不可预测的“垃圾值”。以上就是C语言联合(union)用法案例详解的详细内容,更多关于C语言 联合(union)的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。