Union 在内存中的存储

Storage of Union In Memory

我读到在联合体中,数据成员占用同一块内存。因此,我尝试使用此实现读取英文字母表的 ASCII 码。

union {
    int i;
    char a,b;
}eps;
eps.i=65;
cout<<eps.a<<eps.b;

我得到了 65 的正确输出 (A) 但是,ab 似乎在内存中占据了相同的位置。

问。但是一个整数是 2 个字节,不应该 a 占据前 8 位而 b 其他 8 位吗?

此外,在并集内用多个整数重复上述操作时,它们似乎具有相同的值。

问。那么这是否意味着给定数据类型的每个变量都像一样 对相同数据类型的任何其他变量的引用? (给定简单添加变量 int i,j,k,l.....

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

编辑

我想提一下,在联合内添加任何更多变量时,它只是意味着像 int i,j,k... 一样添加它们,而不是使用将它们包装在 struct 内或以任何其他方式。

正如 Baum mit 在聊天(和评论)中阐明的那样,Here's the discussion 供 other/future 用户查看。

读取一个 union 的成员,但该成员不是您上次写入的成员是未定义的行为。这意味着您的代码可以做任何事情并且争论它的行为是没有意义的。

要执行类型之间的转换,请使用 appropriate cast,而不是联合。

编辑后回答您的问题:

Q. But an integer being 2 bytes, shouldn't a have occupied the first 8 bits and b the other 8 ?

正如您所说,工会的每个成员都共享相同的 space。由于 ab 是不同的成员,因此他们也共享相同的 space(从某种意义上说,他们都住在属于工会的 space 的某个地方)。 union 的实际布局可能如下所示:

byte 0 | byte 1 | byte 2 | byte 3
i        i        i        i
a
b

Q. So does that mean that every variable of a given data type acts as a reference for any other variable for the same data type?

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

Q. Can we only use one (distinct) variable of a given datatype in a union since all others point at the same location?

您可以拥有任意数量的相同类型的成员。他们可能生活在也可能不生活在完全相同的记忆中。没关系,反正你只能访问最后一个写入的。

回想一下,union 类型是一组可供选择的可能性。正式的措辞是它是其字段所属的所有类型的 副产品

union {
  int i;
  char a,b;
}

在语法上等同于:

union {
  int i;
  char a;
  char b;
}

ab 属于同一类型,它们加在一起的贡献并不比彼此单独计算的贡献多。换句话说,b 是多余的。

您需要将 ab 字段包装在 struct 中,以便将它们捆绑为 union.

的另一种选择
union {
  int i;
  struct {
    char a;
    char b;
  };
}

此外,int 类型在大多数平台上是 32 位宽的整数类型,而 char 是 8 位宽的整数类型——我说通常,因为大小没有正式定义更多而不仅仅是 int 大于或等于 char

所以,假设我们有 charint 的通常定义,第二种选择是 16 位宽,编译器有机会把它放在同一个 space 被较大的字段(32 位)占用。

另一个问题是字节顺序可能因平台而异。

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

union {
  int i;
  struct {
    char a;
    char b;
    char c;
    char d;
  };
}

(例如,考虑 IPv4 地址的 int 表示,以及涵盖字节顺序问题的 htons 函数)。

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

为了安全起见,我不会使用 union,而是使用一组函数来通过位掩码提取字节,但是如果您针对的是特定平台,以及以上工会工作...

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

话虽如此,在大多数情况下(并且禁用了优化),您会发现联合的每个部分都从联合的字节 0 开始(不要依赖这个).在您的代码中,union{int i;char a,b;} 表示 "this union could be an integer i, or a char a, or a char b"。您可以使用(正如许多人所建议的那样)union{int i;struct{char a,b;}},它会告诉编译器:"this union could be an integer i, or it could be a structure of characters a and 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 位置),同时使用外部变量跟踪实际存在的内容。它使用的内存比没有联合的等效代码少得多,并且使 setting/getting 安全(我们不需要在所有地方强制转换指针)