C++ 共用体

C++ 中,共用体(union) 是一种特殊的数据结构,可以在同一块内存中存储不同类型的数据,但每次只能存储其中一个。共用体的所有成员共享相同的内存地址,因此共用体的大小取决于其最大成员的大小。共用体的使用非常适合内存资源有限的场景,如嵌入式系统或需要节省内存的情况下。

1、定义声明

共用体是一种特殊的数据类型,可以在相同的内存位置存储不同的数据类型。可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。共用体有时也被称为联合或者联合体。其定义格式为:

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

例如,

#include <iostream>
#include <cstring> // 用于字符串操作
using namespace std;

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    Data data;

    // 使用整数成员
    data.i = 42;
    cout << "Integer: " << data.i << endl;

    // 使用浮点成员,覆盖之前的整数值
    data.f = 3.14;
    cout << "Float: " << data.f << endl;

    // 使用字符串成员,覆盖之前的浮点值
    strcpy(data.str, "Hello, Union");
    cout << "String: " << data.str << endl;

    // 再次尝试访问覆盖的整数和浮点成员(未定义行为)
    cout << "Overwritten Integer: " << data.i << endl;
    cout << "Overwritten Float: " << data.f << endl;

    return 0;
}

1)共用体也是一种自定义类型,可以通过它来创建变量

例如,

#include <stdio.h>
using namespace std;

union data {
    int n;
    char ch;
    double f;
};

int main() {
    // 定义 union 变量
    union data a, b, c;

    // 为 union 成员赋值
    a.n = 42;        // 将整数赋值给 a.n
    b.ch = 'A';      // 将字符赋值给 b.ch
    c.f = 3.14;      // 将浮点数赋值给 c.f

    // 输出 union 成员的值
    printf("a.n: %d\n", a.n);     // 访问整数值
    printf("b.ch: %c\n", b.ch);   // 访问字符值
    printf("c.f: %.2f\n", c.f);   // 访问浮点数值

    // 注意:访问一个 union 的成员时,其他成员的数据可能会被覆盖
    a.f = 1.23;  // 重新为 a.f 赋值
    printf("a.f: %.2f\n", a.f);   // 此时只能正确访问 a.f

    return 0;
}

2)定义共用体的同时创建变量

#include <iostream>
using namespace std;

union Data {
    int n;
    char ch;
    double f;
} a, b, c;

int main() {
    // 使用共用体变量
    a.n = 42;
    b.ch = 'A';
    c.f = 3.14;

    // 输出值
    cout << "a.n = " << a.n << endl;
    cout << "b.ch = " << b.ch << endl;
    cout << "c.f = " << c.f << endl;

    return 0;
}

3)如果不再定义新的变量,也可以将共用体的名字省略

#include <iostream>
using namespace std;

union {
    int n;
    char ch;
    double f;
} a, b, c;

int main() {
    // 使用匿名共用体的变量
    a.n = 42;
    b.ch = 'B';
    c.f = 3.14;

    // 输出值
    cout << "a.n = " << a.n << endl;  // 输出变量 a 的整数值
    cout << "b.ch = " << b.ch << endl;  // 输出变量 b 的字符值
    cout << "c.f = " << c.f << endl;  // 输出变量 c 的浮点值

    return 0;
}

2、访问共用体成员

访问共用体的成员,需要使用成员访问运算符(.)。

例如,

#include <iostream>
using namespace std;
#include <string.h>
union Data
{
   int i;
   float f;
   char  str[20];
};
int main( )
{
   union Data data;        
   data.i = 10;
   data.f = 220.5;
   strcpy( data.str, "C/C++ 语言");
   cout << "data.i : " <<  data.i << endl;
   cout << "data.f : " <<  data.f << endl;
   cout << "data.str : " << data.str << endl;
   return 0;
}

注意:上面示例的输出结果中,共用体的 if 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。

共用体是任何时候只能有一个成员带有值。同一时间应该只用一个成员。

例如,

#include <iostream>
using namespace std;
#include <string.h>
union Data
{
   int i;
   float f;
   char  str[20];
};
int main( )
{
   union Data data;        
   data.i = 10;
   cout <<  "data.i :  " <<  data.i << endl;
   data.f = 220.5;
   cout << "data.f :  " << data.f << endl;
   strcpy( data.str, "C/C++ 语言");
   cout << "data.str :  " <<  data.str << endl;
   return 0;
}

3、共用体的特点及用途

共用体的所有成员共享同一块内存, 每次只能安全访问一个成员,共用体的大小等于最大成员的大小,适合在内存有限或需要高效存储的场景使用。在字节和其他数据类型之间转换时很有用,还可以用于模拟硬件寄存器或处理协议数据。

#include <iostream>
using namespace std;

// 定义共用体
union Data {
    int intValue;      // 整型成员
    float floatValue;  // 浮点型成员
    char charValue;    // 字符型成员
};

int main() {
    Data data;  // 创建共用体变量

    // 存储整数
    data.intValue = 42;
    cout << "Integer value: " << data.intValue << endl;

    // 存储浮点数(覆盖整数)
    data.floatValue = 3.14;
    cout << "Float value: " << data.floatValue << endl;

    // 存储字符(覆盖浮点数)
    data.charValue = 'A';
    cout << "Character value: " << data.charValue << endl;

    // 注意:共用体成员共享内存,数据可能被覆盖
    cout << "After storing character, Integer value: " << data.intValue << endl;

    return 0;
}

4、共用体与结构体的区别

共用体(union)和结构体(struct)是两种数据结构,它们的主要区别在于内存的使用方式以及用途。

特性结构体(struct)共用体(union)
内存分配每个成员独立分配内存所有成员共享同一块内存
大小总大小是所有成员大小之和大小等于最大成员的大小
访问成员可以同时访问多个成员任意时刻只能安全访问一个成员
使用场景用于描述复杂数据结构用于节省内存或实现多种数据形式

5、带构造函数和析构函数的共用体

C++11 开始,共用体可以包含非平凡类型(如 std::string 或类对象)。在这种情况下,需要手动管理构造函数和析构函数。

#include <iostream>
#include <string>
using namespace std;

union MixedData {
    int i;
    double d;
    string s; // 需要手动管理

    MixedData() { new(&s) string("Hello, Union!"); } // 初始化字符串
    ~MixedData() { s.~string(); } // 显式调用析构函数
};

int main() {
    MixedData data;
    cout << data.s << endl;

    data.d = 3.14; // 写入 double,覆盖 string
    cout << "Double: " << data.d << endl;

    return 0;
}

6、内存布局和大小

共用体(union)中所有成员共享同一块内存。内存大小等于最大成员的大小,以确保能够存储共用体的任何成员。起始地址相同,所有成员的地址是相同的(它们共享起始地址)。改变一个成员的值会覆盖其他成员的值。共用体的大小受内存对齐规则的影响,通常为最大成员的大小和系统对齐要求的倍数。

#include <iostream>
using namespace std;

union Example {
    int a;        // 4 字节
    double b;     // 8 字节
    char c[3];    // 3 字节
};

int main() {
    cout << "Size of Example: " << sizeof(Example) << " bytes" << endl;
    return 0;
}

推荐阅读
cjavapy编程之路首页