我是来这里上一些工会的定义如下一个正在进行的项目工作:
/* header.h */
typedef union my_union_t {
float data[4];
struct {
float varA;
float varB;
float varC;
float varD;
};
} my_union;
如果我没有理解好,工会是节省空间,所以的sizeof(my_union_t)=在它的变量MAX。 什么是使用上面的语句,而不是这一块的优势:
typedef struct my_struct {
float varA;
float varB;
float varC;
float varD;
};
不会被分配为他们两个相同的空间?
我怎么能初始化翻,varB ......从my_union?
Answer 1:
联盟并不多为节省空间,但落实和类型 (对于这一点,你就会把union
在某些struct
或class
也有一个鉴别字段,将保持运行时间标记)。 另外,我建议你使用最新的C ++标准,至少C ++ 11 ,因为它有更好的支持工会(例如允许更容易地联合对象和他们的建筑或初始化)。
使用联合的优点是能够以索引n
个浮点(0 <= N <= 3)作为u.data[n]
要指定某些变量声明为工会场my_union u;
只是代码例如u.varB = 3.14;
而你的情况具有相同的效果u.data[1] = 3.14;
当之无愧的联盟的一个很好的例子是一个可变的对象,它可以容纳任何一个int
或string
(你不能使用在这种情况下派生类):
class IntOrString {
bool isint;
union {
int num; // when isint is true
str::string str; // when isint is false
};
public:
IntOrString(int n=0) : isint(true), num(n) {};
IntOrString(std::string s) : isint(false), str(s) {};
IntOrString(const IntOrString& o): isint(o.isint)
{ if (isint) num = o.num; else str = o.str); };
IntOrString(IntOrString&&p) : isint(p.isint)
{ if (isint) num = std::move (p.num);
else str = std::move (p.str); };
~IntOrString() { if (isint) num=0; else str->~std::string(); };
void set (int n)
{ if (!isint) str->~std::string(); isint=true; num=n; };
void set (std::string s) { str = s; isint=false; };
bool is_int() const { return isint; };
int as_int() const { return (isint?num:0; };
const std::string as_string() const { return (isint?"":str;};
};
注意的析构函数的显式调用str
领域。 还要注意,可以安全地使用IntOrString
在标准容器( std::vector<IntOrString>
另见的std ::可选在C未来的版本++(这在概念上是一个标签联合使用void
)
BTW,OCaml中,你只需代码:
type intorstring = Integer of int | String of string;;
你会使用模式匹配 。 如果你想做出可变的,你需要做一个记录或者它的一个参考。
你会更好地利用union
-s在C ++中惯用的方式(请参阅本为一般性咨询)。
Answer 2:
实施状物体的变体(类型字段和数据类型的联合)时工会经常使用,或在执行串行化。
您使用的是联合的方式是一个灾难。
你是假设的struct
在union
被包装的float
与当时之间没有间隙s! 标准保证float data[4];
是连续的,但不是结构元件。 你唯一知道的另一件事是,地址varA
; 是相同的地址data[0]
千万不要以这种方式使用的联合。
至于你的问题:“怎么我可以初始化翻,varB ......从my_union?”。 答案是, 不经由访问结构成员在正常冗长方式data[]
数组。
Answer 3:
其优点是,与工会就可以在两种不同的方式访问相同的内存。
在您的例子工会包含四个浮动。 您可以访问这些彩车的翻,varB ......这可能是更具描述性的名称,或者您可以访问相同的变量数组数据[0],数据[1] ...这可能是在循环中更加有用。
有了工会,你还可以使用不同类型的数据相同的内存,你可能会发现的东西,有用就像写一个函数来告诉你,如果你是在一个大端或小端的CPU。
Answer 4:
不,它不是以节省空间。 它是代表一些二进制数据的各种数据类型的能力。 例如
#include <iostream>
#include <stdint.h>
union Foo{
int x;
struct y
{
unsigned char b0, b1, b2, b3;
};
char z[sizeof(int)];
};
int main()
{
Foo bar;
bar.x = 100;
std::cout << std::hex; // to show number in hexadec repr;
for(size_t i = 0; i < sizeof(int); i++)
{
std::cout << "0x" << (int)bar.z[i] << " "; // int is just to show values as numbers, not a characters
}
return 0;
}
输出: 0x64 0x0 0x0 0x0
的相同的值被存储在结构bar.y,但不是在阵列但在内在张力结构成员。 它的,因为我的机器有一点字节序 。 如果是大的,比输出将得到扭转: 0x0 0x0 0x0 0x64
您可以实现使用相同reinterpret_cast
:
#include <iostream>
#include <stdint.h>
int main()
{
int x = 100;
char * xBytes = reinterpret_cast<char*>(&x);
std::cout << std::hex; // to show number in hexadec repr;
for (size_t i = 0; i < sizeof(int); i++)
{
std::cout << "0x" << (int)xBytes[i] << " "; // (int) is just to show values as numbers, not a characters
}
return 0;
}
它有用,例如,当你需要阅读一些二进制文件,这是写在机器上用不同的字节顺序比你。 你可以只访问值字节组,并根据需要交换这些字节。
此外,它是有用的,当你要处理位域 ,但它的一个完全不同的故事:)
Answer 5:
我想了解工会最好的办法是只给2个普通的实际例子。
第一个例子正在与图像。 想象一下,你有和布置在相当长的缓冲RGB图像。
然而,大多数人会做的,是代表缓冲区作为char*
3的,然后循环它得到了R,G,B。
你可以做的,而不是,是做一个小联盟,并用它来遍历图像缓存:
union RGB
{
char raw[3];
struct
{
char R;
char G;
char B;
} colors;
}
RGB* pixel = buffer[0];
///pixel.colors.R == The red color in the first pixel.
对于工会的另一个非常有用的用途是使用寄存器和位域。
比方说你有一个32位的值,表示某个硬件寄存器,或者什么的。
有时,为了节省空间,可以拆分的32位进位的字段,但你也希望该寄存器的整体表现为一个32位的类型。
这显然是节省很多程序员没有理由使用在所有的位移位计算。
union MySpecialRegister
{
uint32_t register;
struct
{
unsigned int firstField : 5;
unsigned int somethingInTheMiddle : 25;
unsigned int lastField : 6;
} data;
}
// Now you can read the raw register into the register field
// then you can read the fields using the inner data struct
Answer 6:
工会主要用来表示不同的方式相同的数据。 想像一下,如果有一个包含各种数据,诸如用户定义的结构:
typedef struct
{
int x;
int y;
float f;
} my_struct;
现在,你想这种结构通过串行总线发送到另一台计算机。 但是,你的串行总线的硬件只能一次发送1个字节。 并且在接收器侧,有一个x字节硬件缓存。 为了能够在所有发送的数据类型,你可以做一个联盟:
typedef union
{
my_struct s;
uint8_t bytes[sizeof(my_struct)];
} my_union;
现在,您可以使用bytes
,每当你想发送和接收的工会成员,和s
,当你要访问的实际数据成员。
上述概念用于很多硬件相关的编程:数据协议,硬件寄存器的定义,存储器映射,NVM驱动程序等。
注意:当你做这样的事情,要小心填充和对齐! 编译器可以自由地在任何地方插入结构/联合内部填充字节对齐数据。 这可以打破上述概念。
在其他的答案中指出,你也可以使用工会实施“求和输入” /“变种”,但是对于这样的实际应用是非常有限的,如果这样的使用,甚至存在。
Answer 7:
首先:避免工会在接入进入相同的内存,但不同类型的!
工会根本不节省空间。 唯一的定义对同一存储区域的多个名字! 而你只能存储在一个工会在一次性的要素之一。
如果你有
union X
{
int x;
char y[4];
};
你可以存储int或4个字符,但不能同时! 普遍的问题是,没有人知道它的数据实际上是存储在一个工会。 如果保存一个int和读取字符,编译器将不检查,也没有运行时检查。 溶液通常是提供一个结构的附加数据元素,其包含实际存储的数据类型作为枚举的联合。
struct Y
{
enum { IS_CHAR, IS_INT } tinfo;
union
{
int x;
char y[4];
};
}
但在C ++,你总是应该使用类或结构可以从这样的,也许空父类派生:
class Base
{
};
class Int_Type: public Base
{
...
int x;
};
class Char_Type: public Base
{
...
char y[4];
};
所以,你可以装置指针基地实际上可以容纳一个整数,或char类型适合你。 有了虚拟功能,您可以访问编程的面向对象的方式成员。
从巴西莱的答案已经提到的,有用的情况下,可以通过不同的名字访问同一个类型 。
union X
{
struct data
{
float a;
float b;
};
float arr[2];
};
这允许不同的接入方式与同一类型的相同数据。 使用不同类型的存储在相同的内存应该不惜一切避免!
文章来源: C++ understanding Unions and Structs