C++学习笔记-联合体

联合体(Union)是一种特殊的类,它的所有成员共享同一个内存位置存储不同类型的数据。联合体中的所有成员都在同一个内存地址开始,因此,联合体的大小等于最大成员的大小。

基本联合体使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

int main()
{
    struct Union {
        union
        {
            int x;
            int y;
        };
    };

    Union u;
    u.x = 5; // 设置x也等于设置y
    std::cout << "u.x = " << u.x << ", u.y = " << u.y << std::endl;
    
    u.y = 10; // 设置y也会改变x
    std::cout << "u.x = " << u.x << ", u.y = " << u.y << std::endl;
    
    std::cout << "Size of Union: " << sizeof(Union) << " bytes" << std::endl;
    std::cout << "Address of x: " << &u.x << std::endl;
    std::cout << "Address of y: " << &u.y << std::endl;
    
    return 0;
}

复杂联合体示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>

struct Vector2
{
    float x, y;
    
    Vector2(float x = 0, float y = 0) : x(x), y(y) {}
    
    void Print() const
    {
        std::cout << "(" << x << ", " << y << ")";
    }
};

struct Vector4
{
    union
    {
        /*
         * 这两个结构体共享同一个内存,其中Vector 
         * a对应前两个x, y
         * b对应后两个z, w
         * 组成为一个Vector,有2个float,另一个struct,有4个float成员
         */
        struct 
        {
            float x, y, z, w;
        };

        struct
        {
            Vector2 a, b;
        };
    };
    
    Vector4(float x = 0, float y = 0, float z = 0, float w = 0) : x(x), y(y), z(z), w(w) {}
    
    void Print() const
    {
        std::cout << "Vector4(" << x << ", " << y << ", " << z << ", " << w << ")";
    }
};

void Print(const Vector2& vector2)
{
    std::cout << "Vector2: ";
    vector2.Print();
    std::cout << std::endl;
}

int main()
{
    std::cout << "=== Complex Union Example ===" << std::endl;
    
    Vector4 vector4(1.0f, 2.0f, 3.0f, 4.0f);
    
    std::cout << "Original Vector4: ";
    vector4.Print();
    std::cout << std::endl;
    
    std::cout << "Accessing as individual components:" << std::endl;
    std::cout << "x = " << vector4.x << ", y = " << vector4.y 
              << ", z = " << vector4.z << ", w = " << vector4.w << std::endl;
    
    std::cout << "Accessing as Vector2 pairs:" << std::endl;
    Print(vector4.a);  // 前两个分量
    Print(vector4.b);  // 后两个分量
    
    // 修改Vector2会影响Vector4
    vector4.a.x = 10.0f;
    vector4.b.y = 20.0f;
    
    std::cout << "After modifying Vector2 components:" << std::endl;
    vector4.Print();
    std::cout << std::endl;
    
    std::cout << "Size of Vector4: " << sizeof(Vector4) << " bytes" << std::endl;
    std::cout << "Size of 4 floats: " << sizeof(float) * 4 << " bytes" << std::endl;
    
    return 0;
}

联合体的实际应用

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <iostream>
#include <cstdint>
#include <iomanip>

// 颜色表示
union Color
{
    uint32_t value;
    struct {
        uint8_t r, g, b, a;
    } rgba;
    uint8_t components[4];
    
    Color() : value(0) {}
    Color(uint32_t val) : value(val) {}
    Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
    {
        rgba.r = red;
        rgba.g = green;
        rgba.b = blue;
        rgba.a = alpha;
    }
    
    void Print() const
    {
        std::cout << "Color(R:" << (int)rgba.r 
                  << ", G:" << (int)rgba.g 
                  << ", B:" << (int)rgba.b 
                  << ", A:" << (int)rgba.a 
                  << ") = 0x" << std::hex << value << std::dec;
    }
};

// 网络数据包头
union PacketHeader
{
    uint32_t raw;
    struct {
        uint8_t version : 4;
        uint8_t headerLength : 4;
        uint8_t typeOfService;
        uint16_t totalLength;
    } fields;
    uint8_t bytes[4];
    
    PacketHeader(uint32_t data) : raw(data) {}
    
    void Print() const
    {
        std::cout << "Packet Header:" << std::endl;
        std::cout << "  Raw: 0x" << std::hex << raw << std::dec << std::endl;
        std::cout << "  Version: " << (int)fields.version << std::endl;
        std::cout << "  Header Length: " << (int)fields.headerLength << std::endl;
        std::cout << "  Type of Service: " << (int)fields.typeOfService << std::endl;
        std::cout << "  Total Length: " << fields.totalLength << std::endl;
        std::cout << "  Bytes: ";
        for (int i = 0; i < 4; ++i)
        {
            std::cout << "0x" << std::hex << (int)bytes[i] << " ";
        }
        std::cout << std::dec << std::endl;
    }
};

// 浮点数分析
union FloatAnalyzer
{
    float value;
    uint32_t bits;
    struct {
        uint32_t mantissa : 23;
        uint32_t exponent : 8;
        uint32_t sign : 1;
    } ieee754;
    
    FloatAnalyzer(float f) : value(f) {}
    
    void Analyze() const
    {
        std::cout << "Float Analysis for " << value << ":" << std::endl;
        std::cout << "  Bits: 0x" << std::hex << bits << std::dec << std::endl;
        std::cout << "  Sign: " << ieee754.sign << std::endl;
        std::cout << "  Exponent: " << ieee754.exponent 
                  << " (biased), " << (int(ieee754.exponent) - 127) << " (unbiased)" << std::endl;
        std::cout << "  Mantissa: 0x" << std::hex << ieee754.mantissa << std::dec << std::endl;
        
        // 检查特殊值
        if (ieee754.exponent == 0)
        {
            if (ieee754.mantissa == 0)
                std::cout << "  Special: Zero" << std::endl;
            else
                std::cout << "  Special: Denormalized number" << std::endl;
        }
        else if (ieee754.exponent == 255)
        {
            if (ieee754.mantissa == 0)
                std::cout << "  Special: Infinity" << std::endl;
            else
                std::cout << "  Special: NaN" << std::endl;
        }
        else
        {
            std::cout << "  Special: Normal number" << std::endl;
        }
    }
};

void UnionApplications()
{
    std::cout << "=== Union Applications ===" << std::endl;
    
    // 颜色处理
    std::cout << "1. Color Processing:" << std::endl;
    Color red(255, 0, 0, 255);
    red.Print();
    std::cout << std::endl;
    
    Color blue(0x0000FFFF);
    blue.Print();
    std::cout << std::endl;
    
    // 通过数组访问修改颜色
    Color green;
    green.components[0] = 0;    // R
    green.components[1] = 255;  // G
    green.components[2] = 0;    // B
    green.components[3] = 255;  // A
    green.Print();
    std::cout << std::endl;
    
    std::cout << "\n2. Network Packet Header:" << std::endl;
    PacketHeader header(0x45001234);
    header.Print();
    
    std::cout << "\n3. Float Analysis:" << std::endl;
    FloatAnalyzer analyzer1(3.14159f);
    analyzer1.Analyze();
    
    std::cout << std::endl;
    FloatAnalyzer analyzer2(-0.0f);
    analyzer2.Analyze();
    
    std::cout << std::endl;
    FloatAnalyzer analyzer3(std::numeric_limits<float>::infinity());
    analyzer3.Analyze();
}

int main()
{
    UnionApplications();
    return 0;
}

带有构造函数和析构函数的联合体

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <iostream>
#include <string>
#include <new>

// C++11之前,联合体不能包含有构造函数/析构函数的类型
// C++11之后,可以包含,但需要手动管理生命周期

union StringOrInt
{
    int intValue;
    std::string stringValue;
    
    // 需要显式定义构造函数
    StringOrInt() : intValue(0) {}
    
    StringOrInt(int value) : intValue(value) {}
    
    StringOrInt(const std::string& str)
    {
        new(&stringValue) std::string(str);
    }
    
    // 需要显式定义析构函数
    ~StringOrInt()
    {
        // 注意:我们不知道当前存储的是什么类型
        // 在实际使用中需要额外的标记来跟踪类型
    }
};

// 更安全的变体:带类型标记的联合体
class SafeVariant
{
public:
    enum Type { INT, STRING };
    
private:
    Type m_Type;
    union {
        int intValue;
        std::string stringValue;
    };
    
public:
    SafeVariant(int value) : m_Type(INT), intValue(value) {}
    
    SafeVariant(const std::string& str) : m_Type(STRING)
    {
        new(&stringValue) std::string(str);
    }
    
    SafeVariant(const SafeVariant& other) : m_Type(other.m_Type)
    {
        switch (m_Type)
        {
            case INT:
                intValue = other.intValue;
                break;
            case STRING:
                new(&stringValue) std::string(other.stringValue);
                break;
        }
    }
    
    SafeVariant& operator=(const SafeVariant& other)
    {
        if (this != &other)
        {
            Destroy();
            m_Type = other.m_Type;
            switch (m_Type)
            {
                case INT:
                    intValue = other.intValue;
                    break;
                case STRING:
                    new(&stringValue) std::string(other.stringValue);
                    break;
            }
        }
        return *this;
    }
    
    ~SafeVariant()
    {
        Destroy();
    }
    
    Type GetType() const { return m_Type; }
    
    int GetInt() const
    {
        if (m_Type != INT)
            throw std::runtime_error("Not an int");
        return intValue;
    }
    
    const std::string& GetString() const
    {
        if (m_Type != STRING)
            throw std::runtime_error("Not a string");
        return stringValue;
    }
    
    void Print() const
    {
        switch (m_Type)
        {
            case INT:
                std::cout << "Int: " << intValue;
                break;
            case STRING:
                std::cout << "String: \"" << stringValue << "\"";
                break;
        }
    }
    
private:
    void Destroy()
    {
        if (m_Type == STRING)
        {
            stringValue.~string();
        }
    }
};

void ComplexUnionDemo()
{
    std::cout << "=== Complex Union Demo ===" << std::endl;
    
    SafeVariant var1(42);
    var1.Print();
    std::cout << std::endl;
    
    SafeVariant var2("Hello, Union!");
    var2.Print();
    std::cout << std::endl;
    
    // 拷贝构造
    SafeVariant var3 = var2;
    var3.Print();
    std::cout << std::endl;
    
    // 赋值
    var1 = var2;
    var1.Print();
    std::cout << std::endl;
    
    std::cout << "Size of SafeVariant: " << sizeof(SafeVariant) << " bytes" << std::endl;
    std::cout << "Size of std::string: " << sizeof(std::string) << " bytes" << std::endl;
    std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;
}

int main()
{
    ComplexUnionDemo();
    return 0;
}

联合体与std::variant的比较

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <variant>
#include <string>

// 传统联合体方法
union TraditionalUnion
{
    int intVal;
    float floatVal;
    char charVal;
};

// 现代C++17方法:std::variant
using ModernVariant = std::variant<int, float, char, std::string>;

void CompareUnionAndVariant()
{
    std::cout << "=== Union vs std::variant Comparison ===" << std::endl;
    
    // 传统联合体
    std::cout << "1. Traditional Union:" << std::endl;
    TraditionalUnion tu;
    tu.intVal = 42;
    std::cout << "Int value: " << tu.intVal << std::endl;
    
    tu.floatVal = 3.14f;
    std::cout << "Float value: " << tu.floatVal << std::endl;
    std::cout << "Int value after setting float: " << tu.intVal << " (corrupted)" << std::endl;
    
    std::cout << "Size of TraditionalUnion: " << sizeof(TraditionalUnion) << " bytes" << std::endl;
    
    // 现代std::variant
    std::cout << "\n2. std::variant:" << std::endl;
    ModernVariant mv = 42;
    std::cout << "Int value: " << std::get<int>(mv) << std::endl;
    
    mv = 3.14f;
    std::cout << "Float value: " << std::get<float>(mv) << std::endl;
    
    mv = std::string("Hello, variant!");
    std::cout << "String value: " << std::get<std::string>(mv) << std::endl;
    
    // 类型安全访问
    std::cout << "Current type index: " << mv.index() << std::endl;
    std::cout << "Holds string: " << std::holds_alternative<std::string>(mv) << std::endl;
    std::cout << "Holds int: " << std::holds_alternative<int>(mv) << std::endl;
    
    // 访问者模式
    std::visit([](const auto& value) {
        std::cout << "Visitor: " << value << " (type: " << typeid(value).name() << ")" << std::endl;
    }, mv);
    
    std::cout << "Size of std::variant: " << sizeof(ModernVariant) << " bytes" << std::endl;
    
    std::cout << "\nAdvantages of std::variant:" << std::endl;
    std::cout << "- Type safety" << std::endl;
    std::cout << "- Exception safety" << std::endl;
    std::cout << "- Supports complex types" << std::endl;
    std::cout << "- Visitor pattern support" << std::endl;
    std::cout << "- Runtime type information" << std::endl;
    
    std::cout << "\nAdvantages of union:" << std::endl;
    std::cout << "- Smaller memory footprint" << std::endl;
    std::cout << "- Direct memory access" << std::endl;
    std::cout << "- C compatibility" << std::endl;
    std::cout << "- No runtime overhead" << std::endl;
}

int main()
{
    CompareUnionAndVariant();
    return 0;
}

联合体的内存对齐

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <cstdint>

// 不同大小成员的联合体
union AlignmentDemo
{
    char c;
    short s;
    int i;
    long long ll;
    double d;
    
    struct {
        char a;
        int b;
    } nested;
};

// 强制对齐的联合体
union alignas(32) AlignedUnion
{
    char c;
    int i;
    double d;
};

void UnionAlignmentDemo()
{
    std::cout << "=== Union Alignment Demo ===" << std::endl;
    
    AlignmentDemo demo;
    
    std::cout << "Union size: " << sizeof(AlignmentDemo) << " bytes" << std::endl;
    std::cout << "Union alignment: " << alignof(AlignmentDemo) << " bytes" << std::endl;
    
    std::cout << "\nMember sizes:" << std::endl;
    std::cout << "char: " << sizeof(char) << " bytes" << std::endl;
    std::cout << "short: " << sizeof(short) << " bytes" << std::endl;
    std::cout << "int: " << sizeof(int) << " bytes" << std::endl;
    std::cout << "long long: " << sizeof(long long) << " bytes" << std::endl;
    std::cout << "double: " << sizeof(double) << " bytes" << std::endl;
    std::cout << "nested struct: " << sizeof(demo.nested) << " bytes" << std::endl;
    
    std::cout << "\nMember addresses (all should be the same):" << std::endl;
    std::cout << "c: " << (void*)&demo.c << std::endl;
    std::cout << "s: " << (void*)&demo.s << std::endl;
    std::cout << "i: " << (void*)&demo.i << std::endl;
    std::cout << "ll: " << (void*)&demo.ll << std::endl;
    std::cout << "d: " << (void*)&demo.d << std::endl;
    std::cout << "nested: " << (void*)&demo.nested << std::endl;
    
    std::cout << "\nAligned union:" << std::endl;
    std::cout << "Size: " << sizeof(AlignedUnion) << " bytes" << std::endl;
    std::cout << "Alignment: " << alignof(AlignedUnion) << " bytes" << std::endl;
}

int main()
{
    UnionAlignmentDemo();
    return 0;
}

总结

  1. 联合体基础:所有成员共享同一个内存位置,大小等于最大成员的大小
  2. 内存共享
    • 所有成员从同一地址开始
    • 修改一个成员会影响其他成员
    • 只能同时存储一种类型的数据
  3. 实际应用
    • 颜色表示(RGBA分量和32位值)
    • 网络协议解析
    • 浮点数内部结构分析
    • 类型双关(Type Punning)
  4. 现代C++特性
    • 可以包含有构造函数的类型(C++11+)
    • 需要手动管理复杂类型的生命周期
    • std::variant提供类型安全的替代方案
  5. 注意事项
    • 需要额外的类型标记来跟踪当前存储的类型
    • 复杂类型需要手动调用构造函数和析构函数
    • 内存对齐影响联合体大小
  6. 最佳实践
    • 简单类型使用传统联合体
    • 复杂类型考虑使用std::variant
    • 需要C兼容性时使用联合体
    • 注意类型安全和生命周期管理
updatedupdated2025-09-202025-09-20