联合体(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;
}