联合体(Union)是 C++ 中的一种特殊数据类型,它允许在同一内存位置存储不同类型的数据。联合体的所有成员共享同一块内存空间,因此在任何时刻只能存储其中一个成员的值。

联合体的基本概念

联合体的定义和特性

#include <iostream>
#include <string>
#include <cstring>
 
// 基本联合体定义
union BasicUnion {
    int intValue;
    float floatValue;
    char charValue;
};
 
// 带有不同大小成员的联合体
union DataUnion {
    char byte;          // 1 字节
    short shortValue;   // 2 字节
    int intValue;       // 4 字节
    long longValue;     // 8 字节
    double doubleValue; // 8 字节
    char array[16];     // 16 字节
};
 
int main() {
    std::cout << "=== 联合体基本概念 ===" << std::endl;
    
    // 1. 联合体大小等于最大成员的大小
    std::cout << "BasicUnion 大小: " << sizeof(BasicUnion) << " 字节" << std::endl;
    std::cout << "DataUnion 大小: " << sizeof(DataUnion) << " 字节" << std::endl;
    
    // 2. 所有成员共享同一内存地址
    BasicUnion bu;
    std::cout << "\nBasicUnion 成员地址:" << std::endl;
    std::cout << "intValue 地址: " << &bu.intValue << std::endl;
    std::cout << "floatValue 地址: " << &bu.floatValue << std::endl;
    std::cout << "charValue 地址: " << &bu.charValue << std::endl;
    
    // 3. 修改一个成员会影响其他成员
    bu.intValue = 0x12345678;
    std::cout << "\n设置 intValue = 0x12345678 后:" << std::endl;
    std::cout << "intValue: 0x" << std::hex << bu.intValue << std::endl;
    std::cout << "charValue: 0x" << std::hex << (int)bu.charValue << std::endl;
    
    bu.floatValue = 3.14f;
    std::cout << "\n设置 floatValue = 3.14 后:" << std::endl;
    std::cout << "floatValue: " << std::dec << bu.floatValue << std::endl;
    std::cout << "intValue: 0x" << std::hex << bu.intValue << std::endl;
    
    return 0;
}

联合体的内存布局

#include <iostream>
#include <iomanip>
 
union MemoryLayout {
    struct {
        unsigned char byte0;
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
    } bytes;
    
    unsigned int fullValue;
    
    struct {
        unsigned short low;
        unsigned short high;
    } words;
};
 
void printMemoryLayout(const MemoryLayout& ml) {
    std::cout << "联合体内存布局分析:" << std::endl;
    std::cout << "fullValue: 0x" << std::hex << std::setw(8) << std::setfill('0') 
              << ml.fullValue << std::endl;
    
    std::cout << "字节分解: ";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)ml.bytes.byte3 << " ";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)ml.bytes.byte2 << " ";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)ml.bytes.byte1 << " ";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)ml.bytes.byte0 << std::endl;
    
    std::cout << "字分解: high=0x" << std::hex << std::setw(4) << std::setfill('0') << ml.words.high
              << ", low=0x" << std::hex << std::setw(4) << std::setfill('0') << ml.words.low << std::endl;
}
 
int main() {
    std::cout << "=== 联合体内存布局 ===" << std::endl;
    
    MemoryLayout ml;
    
    // 设置完整值
    ml.fullValue = 0x12345678;
    std::cout << "设置 fullValue = 0x12345678:" << std::endl;
    printMemoryLayout(ml);
    
    // 通过字节修改
    std::cout << "\n修改 byte1 = 0xAB:" << std::endl;
    ml.bytes.byte1 = 0xAB;
    printMemoryLayout(ml);
    
    // 通过字修改
    std::cout << "\n修改 words.high = 0xCDEF:" << std::endl;
    ml.words.high = 0xCDEF;
    printMemoryLayout(ml);
    
    return 0;
}

联合体的实际应用

类型转换和数据解析

#include <iostream>
#include <iomanip>
#include <cstring>
 
// 用于浮点数的位级操作
union FloatBits {
    float floatValue;
    struct {
        unsigned int mantissa : 23;  // 尾数
        unsigned int exponent : 8;   // 指数
        unsigned int sign : 1;       // 符号位
    } bits;
    unsigned int intValue;
};
 
// 用于网络字节序转换
union NetworkData {
    struct {
        unsigned char byte0;
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
    } bytes;
    unsigned int value;
};
 
// 用于颜色表示
union Color {
    struct {
        unsigned char red;
        unsigned char green;
        unsigned char blue;
        unsigned char alpha;
    } rgba;
    unsigned int value;
};
 
void analyzeFloat(float f) {
    FloatBits fb;
    fb.floatValue = f;
    
    std::cout << "浮点数 " << f << " 的位级分析:" << std::endl;
    std::cout << "  完整位模式: 0x" << std::hex << std::setw(8) << std::setfill('0') 
              << fb.intValue << std::endl;
    std::cout << "  符号位: " << fb.bits.sign << std::endl;
    std::cout << "  指数: " << std::dec << fb.bits.exponent 
              << " (偏移后: " << (fb.bits.exponent - 127) << ")" << std::endl;
    std::cout << "  尾数: 0x" << std::hex << fb.bits.mantissa << std::endl;
}
 
unsigned int hostToNetwork(unsigned int hostValue) {
    NetworkData nd;
    nd.value = hostValue;
    
    // 手动字节序转换(大端序)
    NetworkData result;
    result.bytes.byte0 = nd.bytes.byte3;
    result.bytes.byte1 = nd.bytes.byte2;
    result.bytes.byte2 = nd.bytes.byte1;
    result.bytes.byte3 = nd.bytes.byte0;
    
    return result.value;
}
 
void demonstrateColor() {
    Color color;
    
    // 设置 RGBA 值
    color.rgba.red = 255;
    color.rgba.green = 128;
    color.rgba.blue = 64;
    color.rgba.alpha = 255;
    
    std::cout << "颜色值:" << std::endl;
    std::cout << "  RGBA: (" << (int)color.rgba.red << ", " 
              << (int)color.rgba.green << ", " 
              << (int)color.rgba.blue << ", " 
              << (int)color.rgba.alpha << ")" << std::endl;
    std::cout << "  十六进制: 0x" << std::hex << std::setw(8) << std::setfill('0') 
              << color.value << std::endl;
}
 
int main() {
    std::cout << "=== 联合体实际应用 ===" << std::endl;
    
    // 浮点数分析
    analyzeFloat(3.14159f);
    std::cout << std::endl;
    analyzeFloat(-0.5f);
    std::cout << std::endl;
    
    // 网络字节序转换
    unsigned int hostValue = 0x12345678;
    unsigned int networkValue = hostToNetwork(hostValue);
    std::cout << "字节序转换:" << std::endl;
    std::cout << "  主机序: 0x" << std::hex << hostValue << std::endl;
    std::cout << "  网络序: 0x" << std::hex << networkValue << std::endl;
    std::cout << std::endl;
    
    // 颜色演示
    demonstrateColor();
    
    return 0;
}

变体数据类型(Tagged Union)

#include <iostream>
#include <string>
#include <cstring>
 
// 数据类型标识
enum DataType {
    TYPE_INT,
    TYPE_FLOAT,
    TYPE_STRING,
    TYPE_BOOL
};
 
// 带标签的联合体
struct Variant {
    DataType type;
    
    union {
        int intValue;
        float floatValue;
        char stringValue[32];
        bool boolValue;
    } data;
    
    // 构造函数
    Variant() : type(TYPE_INT) {
        data.intValue = 0;
    }
    
    Variant(int value) : type(TYPE_INT) {
        data.intValue = value;
    }
    
    Variant(float value) : type(TYPE_FLOAT) {
        data.floatValue = value;
    }
    
    Variant(const char* value) : type(TYPE_STRING) {
        strncpy(data.stringValue, value, sizeof(data.stringValue) - 1);
        data.stringValue[sizeof(data.stringValue) - 1] = '\0';
    }
    
    Variant(bool value) : type(TYPE_BOOL) {
        data.boolValue = value;
    }
    
    // 获取值的方法
    int getInt() const {
        if (type == TYPE_INT) return data.intValue;
        throw std::runtime_error("类型不匹配:期望 int");
    }
    
    float getFloat() const {
        if (type == TYPE_FLOAT) return data.floatValue;
        throw std::runtime_error("类型不匹配:期望 float");
    }
    
    const char* getString() const {
        if (type == TYPE_STRING) return data.stringValue;
        throw std::runtime_error("类型不匹配:期望 string");
    }
    
    bool getBool() const {
        if (type == TYPE_BOOL) return data.boolValue;
        throw std::runtime_error("类型不匹配:期望 bool");
    }
    
    // 显示值
    void display() const {
        switch (type) {
            case TYPE_INT:
                std::cout << "int: " << data.intValue;
                break;
            case TYPE_FLOAT:
                std::cout << "float: " << data.floatValue;
                break;
            case TYPE_STRING:
                std::cout << "string: \"" << data.stringValue << "\"";
                break;
            case TYPE_BOOL:
                std::cout << "bool: " << (data.boolValue ? "true" : "false");
                break;
        }
    }
    
    // 获取类型名称
    const char* getTypeName() const {
        switch (type) {
            case TYPE_INT: return "int";
            case TYPE_FLOAT: return "float";
            case TYPE_STRING: return "string";
            case TYPE_BOOL: return "bool";
            default: return "unknown";
        }
    }
};
 
// 处理变体数组
void processVariants(Variant variants[], int count) {
    std::cout << "处理变体数组:" << std::endl;
    
    for (int i = 0; i < count; ++i) {
        std::cout << "[" << i << "] " << variants[i].getTypeName() << ": ";
        variants[i].display();
        std::cout << std::endl;
    }
}
 
int main() {
    std::cout << "=== 带标签的联合体 ===" << std::endl;
    
    // 创建不同类型的变体
    Variant variants[] = {
        Variant(42),
        Variant(3.14159f),
        Variant("Hello World"),
        Variant(true),
        Variant(-100),
        Variant(false)
    };
    
    int count = sizeof(variants) / sizeof(variants[0]);
    
    // 显示所有变体
    processVariants(variants, count);
    
    // 类型安全访问
    std::cout << "\n类型安全访问:" << std::endl;
    try {
        std::cout << "variants[0] as int: " << variants[0].getInt() << std::endl;
        std::cout << "variants[1] as float: " << variants[1].getFloat() << std::endl;
        std::cout << "variants[2] as string: " << variants[2].getString() << std::endl;
        
        // 这会抛出异常
        std::cout << "variants[0] as float: " << variants[0].getFloat() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "错误: " << e.what() << std::endl;
    }
    
    return 0;
}

高级联合体应用

类型安全的联合体(Tagged Union)

#include <iostream>
#include <string>
#include <variant>  // C++17
 
// 传统的标记联合体
class SafeUnion {
public:
    enum Type {
        INTEGER,
        FLOATING,
        CHARACTER,
        STRING
    };
    
private:
    Type currentType;
    union {
        int intValue;
        double doubleValue;
        char charValue;
        std::string* stringValue;  // 指针,因为string有构造函数
    } data;
    
public:
    SafeUnion() : currentType(INTEGER) {
        data.intValue = 0;
    }
    
    // 拷贝构造函数
    SafeUnion(const SafeUnion& other) : currentType(other.currentType) {
        switch (currentType) {
            case INTEGER:
                data.intValue = other.data.intValue;
                break;
            case FLOATING:
                data.doubleValue = other.data.doubleValue;
                break;
            case CHARACTER:
                data.charValue = other.data.charValue;
                break;
            case STRING:
                data.stringValue = new std::string(*other.data.stringValue);
                break;
        }
    }
    
    // 析构函数
    ~SafeUnion() {
        if (currentType == STRING) {
            delete data.stringValue;
        }
    }
    
    // 设置整数值
    void setInt(int value) {
        cleanup();
        currentType = INTEGER;
        data.intValue = value;
    }
    
    // 设置浮点值
    void setDouble(double value) {
        cleanup();
        currentType = FLOATING;
        data.doubleValue = value;
    }
    
    // 设置字符值
    void setChar(char value) {
        cleanup();
        currentType = CHARACTER;
        data.charValue = value;
    }
    
    // 设置字符串值
    void setString(const std::string& value) {
        cleanup();
        currentType = STRING;
        data.stringValue = new std::string(value);
    }
    
    // 获取当前类型
    Type getType() const {
        return currentType;
    }
    
    // 安全获取值
    int getInt() const {
        if (currentType != INTEGER) {
            throw std::runtime_error("当前不是整数类型");
        }
        return data.intValue;
    }
    
    double getDouble() const {
        if (currentType != FLOATING) {
            throw std::runtime_error("当前不是浮点类型");
        }
        return data.doubleValue;
    }
    
    char getChar() const {
        if (currentType != CHARACTER) {
            throw std::runtime_error("当前不是字符类型");
        }
        return data.charValue;
    }
    
    const std::string& getString() const {
        if (currentType != STRING) {
            throw std::runtime_error("当前不是字符串类型");
        }
        return *data.stringValue;
    }
    
    // 打印当前值
    void print() const {
        switch (currentType) {
            case INTEGER:
                std::cout << "整数: " << data.intValue;
                break;
            case FLOATING:
                std::cout << "浮点数: " << data.doubleValue;
                break;
            case CHARACTER:
                std::cout << "字符: '" << data.charValue << "'";
                break;
            case STRING:
                std::cout << "字符串: \"" << *data.stringValue << "\"";
                break;
        }
        std::cout << std::endl;
    }
    
private:
    void cleanup() {
        if (currentType == STRING) {
            delete data.stringValue;
        }
    }
};
 
// 使用 std::variant (C++17) - 更现代的方法
void demonstrateVariant() {
    std::cout << "\n=== std::variant 示例 (C++17) ===" << std::endl;
    
    std::variant<int, double, char, std::string> modernUnion;
    
    // 设置不同类型的值
    modernUnion = 42;
    std::cout << "整数: " << std::get<int>(modernUnion) << std::endl;
    
    modernUnion = 3.14159;
    std::cout << "浮点数: " << std::get<double>(modernUnion) << std::endl;
    
    modernUnion = 'X';
    std::cout << "字符: " << std::get<char>(modernUnion) << std::endl;
    
    modernUnion = std::string("Hello, World!");
    std::cout << "字符串: " << std::get<std::string>(modernUnion) << std::endl;
    
    // 使用 visitor 模式
    auto visitor = [](const auto& value) {
        std::cout << "访问者模式输出: " << value << std::endl;
    };
    
    std::visit(visitor, modernUnion);
    
    // 检查当前类型
    std::cout << "当前类型索引: " << modernUnion.index() << std::endl;
    std::cout << "是否为字符串: " << std::holds_alternative<std::string>(modernUnion) << std::endl;
}

联合体在网络编程中的应用

#include <iostream>
#include <cstring>
#include <iomanip>
 
// IP地址的不同表示方式
union IPAddress {
    uint32_t address;           // 32位整数表示
    uint8_t bytes[4];          // 4个字节表示
    struct {
        uint8_t a, b, c, d;    // 点分十进制表示
    } parts;
    
    IPAddress(uint32_t addr = 0) : address(addr) {}
    
    IPAddress(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
        parts.a = a;
        parts.b = b;
        parts.c = c;
        parts.d = d;
    }
    
    void print() const {
        std::cout << "IP地址: " 
                  << static_cast<int>(parts.a) << "."
                  << static_cast<int>(parts.b) << "."
                  << static_cast<int>(parts.c) << "."
                  << static_cast<int>(parts.d) << std::endl;
        
        std::cout << "32位表示: 0x" << std::hex << address << std::dec << std::endl;
        std::cout << "字节数组: [";
        for (int i = 0; i < 4; ++i) {
            std::cout << static_cast<int>(bytes[i]);
            if (i < 3) std::cout << ", ";
        }
        std::cout << "]" << std::endl;
    }
};
 
// 数据包头部结构
struct PacketHeader {
    union {
        uint16_t flags;
        struct {
            uint16_t version : 4;      // 版本号 (4位)
            uint16_t headerLen : 4;    // 头部长度 (4位)
            uint16_t typeOfService : 8; // 服务类型 (8位)
        } fields;
    };
    
    uint16_t totalLength;
    uint16_t identification;
    IPAddress sourceIP;
    IPAddress destIP;
    
    PacketHeader() : flags(0), totalLength(0), identification(0) {}
    
    void setVersion(uint8_t ver) {
        fields.version = ver & 0x0F;
    }
    
    void setHeaderLength(uint8_t len) {
        fields.headerLen = len & 0x0F;
    }
    
    void setTypeOfService(uint8_t tos) {
        fields.typeOfService = tos;
    }
    
    void print() const {
        std::cout << "=== 数据包头部信息 ===" << std::endl;
        std::cout << "版本: " << fields.version << std::endl;
        std::cout << "头部长度: " << fields.headerLen << std::endl;
        std::cout << "服务类型: " << static_cast<int>(fields.typeOfService) << std::endl;
        std::cout << "总长度: " << totalLength << std::endl;
        std::cout << "标识: " << identification << std::endl;
        
        std::cout << "源IP: ";
        sourceIP.print();
        std::cout << "目标IP: ";
        destIP.print();
    }
};
 
int main() {
    std::cout << "=== 安全联合体示例 ===" << std::endl;
    
    SafeUnion safeVar;
    
    try {
        safeVar.setInt(100);
        safeVar.print();
        
        safeVar.setDouble(3.14159);
        safeVar.print();
        
        safeVar.setChar('A');
        safeVar.print();
        
        safeVar.setString("Hello, Union!");
        safeVar.print();
        
        // 尝试错误的类型访问
        // int wrongValue = safeVar.getInt(); // 这会抛出异常
        
    } catch (const std::exception& e) {
        std::cout << "错误: " << e.what() << std::endl;
    }
    
    // 演示 std::variant
    demonstrateVariant();
    
    std::cout << "\n=== IP地址联合体示例 ===" <

联合体与结构体的比较

联合体(union)和结构体(struct)是C++中两种重要的数据组织方式,它们在内存使用和数据存储方面有着根本性的差异。

内存使用对比

#include <iostream>
#include <string>
#include <iomanip>
 
// 结构体版本 - 所有成员同时存在
struct PersonStruct {
    int id;
    float salary;
    char grade;
    bool isActive;
};
 
// 联合体版本 - 只能存储一种数据
union PersonUnion {
    int id;
    float salary;
    char grade;
    bool isActive;
};
 
// 混合使用:结构体包含联合体
struct Employee {
    std::string name;
    
    enum InfoType {
        ID_INFO,
        SALARY_INFO,
        GRADE_INFO,
        STATUS_INFO
    } currentInfo;
    
    union {
        int id;
        float salary;
        char grade;
        bool isActive;
    } info;
    
    Employee(const std::string& n) : name(n), currentInfo(ID_INFO) {
        info.id = 0;
    }
    
    void setId(int value) {
        currentInfo = ID_INFO;
        info.id = value;
    }
    
    void setSalary(float value) {
        currentInfo = SALARY_INFO;
        info.salary = value;
    }
    
    void setGrade(char value) {
        currentInfo = GRADE_INFO;
        info.grade = value;
    }
    
    void setStatus(bool value) {
        currentInfo = STATUS_INFO;
        info.isActive = value;
    }
    
    void displayInfo() const {
        std::cout << "员工: " << name << " - ";
        switch (currentInfo) {
            case ID_INFO:
                std::cout << "ID: " << info.id;
                break;
            case SALARY_INFO:
                std::cout << "薪资: $" << std::fixed << std::setprecision(2) << info.salary;
                break;
            case GRADE_INFO:
                std::cout << "等级: " << info.grade;
                break;
            case STATUS_INFO:
                std::cout << "状态: " << (info.isActive ? "活跃" : "非活跃");
                break;
        }
        std::cout << std::endl;
    }
};
 
// 演示内存使用差异
void demonstrateMemoryUsage() {
    std::cout << "=== 内存使用对比 ===" << std::endl;
    std::cout << "PersonStruct 大小: " << sizeof(PersonStruct) << " 字节" << std::endl;
    std::cout << "PersonUnion 大小: " << sizeof(PersonUnion) << " 字节" << std::endl;
    std::cout << "Employee 大小: " << sizeof(Employee) << " 字节" << std::endl;
    
    std::cout << "\n各数据类型大小:" << std::endl;
    std::cout << "int: " << sizeof(int) << " 字节" << std::endl;
    std::cout << "float: " << sizeof(float) << " 字节" << std::endl;
    std::cout << "char: " << sizeof(char) << " 字节" << std::endl;
    std::cout << "bool: " << sizeof(bool) << " 字节" << std::endl;
    std::cout << "std::string: " << sizeof(std::string) << " 字节" << std::endl;
}
 
int main() {
    demonstrateMemoryUsage();
    
    std::cout << "\n=== 结构体使用示例 ===" << std::endl;
    PersonStruct person1;
    person1.id = 1001;
    person1.salary = 5000.50f;
    person1.grade = 'A';
    person1.isActive = true;
    
    std::cout << "结构体可以同时存储所有数据:" << std::endl;
    std::cout << "ID: " << person1.id << std::endl;
    std::cout << "薪资: $" << person1.salary << std::endl;
    std::cout << "等级: " << person1.grade << std::endl;
    std::cout << "状态: " << (person1.isActive ? "活跃" : "非活跃") << std::endl;
    
    std::cout << "\n=== 联合体使用示例 ===" << std::endl;
    PersonUnion person2;
    
    person2.id = 2001;
    std::cout << "设置ID后: " << person2.id << std::endl;
    
    person2.salary = 6000.75f;
    std::cout << "设置薪资后: $" << person2.salary << std::endl;
    std::cout << "此时ID值变为: " << person2.id << " (数据被覆盖)" << std::endl;
    
    person2.grade = 'B';
    std::cout << "设置等级后: " << person2.grade << std::endl;
    std::cout << "此时薪资值变为: $" << person2.salary << " (数据被覆盖)" << std::endl;
    
    std::cout << "\n=== 混合使用示例 ===" << std::endl;
    Employee emp("张三");
    
    emp.setId(3001);
    emp.displayInfo();
    
    emp.setSalary(7500.25f);
    emp.displayInfo();
    
    emp.setGrade('A');
    emp.displayInfo();
    
    emp.setStatus(true);
    emp.displayInfo();
    
    return 0;
}