例如,
union
{
float fl,
unsigned int uinteg,
char ch,
int integ
} foo;
所有这些都混合存储在一个内存区域中。有什么意义,因为一旦你设置了值
foo.fl = 3.14f;
foo.uinteg = 666;
foo.ch = 'a';
foo.int = -25;
将不再可能将它们取回 - 一切都会混淆吗?一种节省几个字节或几个时钟周期并且仍然可读的方法?不要写 4 个不同的函数,而是写一个接受union
并已经决定在其中做什么的函数?void *
这样的话,接受再转成自己需要的类型不是更容易吗?作为“Just cast”的示例,我将给出以下代码:
经典例子:
typedef enum { STR, INT } tType;
typedef struct {
tType typ; // typ is separate.
union {
int ival; // ival and sval occupy same memory.
char *sval;
};
} tVal;
void printer(tVal uni)
{
if (uni.type == INTEGER) // blah-blah
uni.ival // Используем integer
else
ini.sval // В противном случае
}
打印机函数可以改写成这样:
void printer(void* data, tType typ)
{
if (tType == INTEGER)
{
(int*)data // Чего-то делаем
}
}
另一个例子:
union
{
int a;
int b;
int c;
} bar;
bar.a = 20;
bar.b = 50; // Значение a потеряли :(
同样,如果我可以先创建一个单独的变量int a = 20;
然后更改其值a = 50;
并且效果完全相同,那有什么意义呢?看起来像是强大的巫术。
Union(联合)用于两种情况:
创建一种“通用”数据类型,不仅能够存储一种,而且能够存储一种预定义类型。为此,将整数字段添加到联合中,指示当前存储的数据类型:
现实生活中此类用途的一个示例是
VARIANT
来自 Windows API 的结构。换句话说,这是 modern 等的前身
boost::variant
。QVariant
然而,上述类可以存储非原始类型(具有构造函数、析构函数和复制运算符),但union
不能。在不兼容的类型之间进行转换。传统上,出于这些目的,使用转换运算符
(T)
, 或reinterpret_cast<>
。但是,这些方法很危险,因为它们违反了严格的别名规则,因此会产生未定义(即不可预测)的行为。正确的转换方式是
memcpy
(编译器抛出哪种调用)或使用union
-a。更新:注意!via 转换
union
仅在 C 中有效,在 C++ 中无效。在回答“访问不活跃的工会成员和未定义的行为?”这个问题时 参考标准的以下段落:c11
c++11(没有类型双关的明确许可)
一种实际用途
union
是访问传输信息的位。假设信息是逐位传输的,每次 32 位。在读/写的时候,我们可以立即操作
uint32_t
,但是立即出现对位的访问。以下是 Björn Stroustrup 曾经对我提出的类似问题的回答:
换句话说,在我们这个时代通常不需要它们。
如果我没理解错的话,这是内存中变量的组合。有时,如果您想以不同的方式查看变量或只需要访问变量的一部分,这会非常方便……
例如String可以解释为一个byte[]数组或者一个矩形的byte[][2]数组,一个整数可以分解为byte_lo和byte_hi
当需要处理以“原始”形式出现的数据时(例如,当通过 CAN 网络或以太网传输时),union在使用微控制器时非常有用。
例如,当通过信息网络传输时,可能有两种类型的消息:
我们创建一个协会
创建一个变量
我们将接收到的数据记录在一个变量
data.data
中,并使用data.msg1
或值解释消息中的数据data.msg2
。您也可以通过直接将指向“原始”数据的点转换为其中一种消息类型的指针并使用消息字段来不使用联合,但是当启用优化时(例如 -O2 或 -O3),此类代码将停止工作。声明变量的时候需要处处加上关键字
volatile
。如前所述,union 在使用微控制器时非常有用,例如,为了节省内存,您可以声明一个标志(或值)结构,以位为单位绑定大小并添加一个与所需位大小相当的变量。