C++ 内存中的并集存储

C++ 内存中的并集存储,c++,unions,C++,Unions,我读到在联合中,数据成员占用相同的内存块。所以,我试着用这个实现读取英语字母表的ASCII码 union { int i; char a,b; }eps; eps.i=65; cout<<eps.a<<eps.b; 联合{ int i; 字符a,b; }每股收益; 每股收益i=65; cout读取不是您上次写入的联盟的成员是未定义的行为。这意味着您的代码可以做任何事情,而争论其行为是没有意义的 要在类型之间执行转换,请使用,而不是联合 编辑后回答您的问题

我读到在联合中,数据成员占用相同的内存块。所以,我试着用这个实现读取英语字母表的ASCII码

union {
    int i;
    char a,b;
}eps;
eps.i=65;
cout<<eps.a<<eps.b;
联合{
int i;
字符a,b;
}每股收益;
每股收益i=65;

cout读取不是您上次写入的
联盟的成员是未定义的行为。这意味着您的代码可以做任何事情,而争论其行为是没有意义的

要在类型之间执行转换,请使用,而不是联合

编辑后回答您的问题:

但是一个2字节的整数,a不应该占用前8位,b不应该占用其他8位吗

正如你所说,工会的每个成员都共享同一个空间。由于
a
b
是不同的成员,它们也共享相同的空间(从这个意义上说,它们都生活在属于联盟的空间的某个地方)。联合体的实际布局可能如下所示:

byte 0 | byte 1 | byte 2 | byte 3
i        i        i        i
a
b
那么,这是否意味着给定数据类型的每个变量都充当同一数据类型的任何其他变量的引用

不,同一时间的成员不作为彼此的参考。如果您有对对象的引用,则可以通过该引用可靠地访问该对象。同一类型的两个成员可能使用完全相同的内存,但您不能依赖于此。我上面提到的规则仍然适用

由于所有其他变量都指向同一位置,我们能否在联合中只使用给定数据类型的一个(不同的)变量


您可以拥有任意数量的相同类型的成员。他们可能生活在同一个记忆中,也可能不生活在同一个记忆中。这并不重要,因为您无论如何只能访问最后写入的一个。

回想一下,
联合类型是一组可选的可能性。正式的表述是,它是其领域所属所有类型的共同产物

union {
  int i;
  char a,b;
}
在语法上等同于:

union {
  int i;
  char a;
  char b;
}
a
b
属于同一类型,它们在一起的贡献并不比各自的贡献大。换句话说,
b
是冗余的

您需要将
a
b
字段包装在
struct
中,以便将它们作为
联合
的一个备选方案进行捆绑

union {
  int i;
  struct {
    char a;
    char b;
  };
}
此外,
int
类型在大多数平台上是一种32位宽的整数类型,
char
是一种8位宽的整数类型——我通常这么说,因为大小的正式定义不仅仅是
int
大于或等于
char

因此,假设我们有
char
int
的常规定义,第二种选择是16位宽,编译器有机会将它放在它想要的地方,放在较大字段(32位)占用的相同空间内

另一个问题是字节顺序,不同平台的字节顺序可能不同

您也许可以通过在结构中填充缺失的字节以达到32位来让它工作(实际上它几乎总是工作的):

union {
  int i;
  struct {
    char a;
    char b;
    char c;
    char d;
  };
}
(例如,考虑IPv4地址的
int
表示,以及覆盖字节排序问题的
htons
函数)

然而,最终的规则是由C语言规范规定的,它没有指定这一点


为了安全起见,我希望使用一组函数来通过位屏蔽提取字节,而不是使用
联合,但是如果您针对的是特定平台,并且上述联合有效…

您误解了联合的用途。他们不保证以任何可预测的方式共享内存。它们只是提供了一种方式,表示一个实体可以存储多种类型信息中的一种。如果设置了一种类型,则其他类型未定义(可以是任何类型,甚至可以是与输入的数据无关的类型)。它们如何共享内存取决于编译器,可能取决于启用的优化(例如内存对齐规则)

综上所述,在大多数情况下(并且禁用了优化),您会发现联合的每个部分都从联合的字节0开始(不依赖此)。在您的代码中,
union{int i;char a,b;}
表示“这个union可以是整数i,或者char a,或者char b”。您可以使用(正如许多人所建议的那样),
union{int i;struct{char a,b;}}
,这将告诉编译器:“这个union可以是整数i,也可以是字符a和b的结构”

因此,从一种类型转换到另一种类型或其组件字节不是联合的工作。相反,您应该使用强制类型转换

那么你会在哪里使用工会呢?下面是一个例子:

struct {
    int type; // maybe 0 = int, 1 = long, ...
    union {
        char c;
        int i;
        long l;
        float f;
        double d;
        struct {
            int x;
            int y;
        } pos;
        // etc.
    } value;
};

对于这样的对象,我们可以动态存储任何类型的数字(或者我们可能需要的任何其他数字,如本例中的2D位置),同时使用外部变量跟踪实际存在的数字。它使用的内存比没有联合的等效代码要少得多,并且使设置/变得安全(我们不需要到处投射指针)

使用匿名结构来包装a和b。
char a,b
只是
char a的简写;字符b我的观点是,如果你把它完整地写出来,你可能会看到编译器只是看到一个由三部分组成的并集,而不是由int和2个字符组成的结构组成的并集。那么它们是如何存储在内存中的呢?字节0->3 i;字节0a;字节0。你为什么认为
b
应该在
a
之后?这是一个由三个元素组成的联合体,
i
a
b
。这意味着永远不应该使用这种方法。@KunalGupta在您写入
eps.i
之后,您只能读取
eps.i
,直到您写入另一个成员为止。那么您只能读取该成员。那么,一次只能读取一个要进行I/O的成员?@KunalGupta规则很简单:您只能读取上次分配给的成员。不能使用
联合
转换