在内存中存储联盟

时间:2015-02-08 14:32:22

标签: c++ unions

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

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

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

Q值。但是一个2字节的整数,a不应该占用前8位,而b占用其他8位?

此外,在联合内部使用多个整数重复上述内容时,它们似乎具有相同的值。

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

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

修改

我想提一下,虽然在联盟中添加了更多变量,但它只是意味着像int i,j,k...那样添加它们而不是使用struct或其他任何方式包装它们。

正如Baum mit在聊天(和评论)中澄清的那样,Here's the discussion可供其他/未来的用户查看。

3 个答案:

答案 0 :(得分:7)

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

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

在编辑后回答您的问题:

  

Q值。但是一个整数是2个字节,不应该占用前8位而b是另外8个?

正如你所说,工会的每个成员都拥有相同的空间。由于ab是不同的成员,因此它们也共享相同的空间(从某种意义上说它们都存在于属于联合的空间中)。联合的实际布局可能如下所示:

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

Q值。那么这是否意味着给定数据类型的每个变量都充当相同数据类型的任何其他变量的引用?

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

  

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

您可以拥有任意数量的相同类型的成员。它们可能存在也可能不存在于完全相同的存储器中。这没关系,因为你只能访问最后写的那个。

答案 1 :(得分:3)

回想一下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位宽,编译器有机会将它放在它想要的相同空间内。较大的字段(32位)。

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

你可以通过用缺少的字节填充结构来达到32位来使它工作(并且在实践中它几乎总是有效):

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

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

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

为了安全起见,而不是使用union,我会选择一组函数来逐位掩盖,但如果你的目标是特定的平台,那么上面的联合工作...

答案 2 :(得分:3)

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

说了这么多,在大多数情况下(并且禁用了优化),你会发现联合的每个部分都从联合的第0个字节开始(不依赖于此)。在您的代码中,union{int i;char a,b;}表示“此联合可以是整数i,或char a或char b”。您可以使用(正如许多人所建议的)union{int i;struct{char a,b;}},这将告诉编译器:“此联合可以是整数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位置),同时使用外部变量跟踪实际存在的内容。它使用的内存比没有联合的等效代码少得多,并且使得设置/获得安全(我们不需要在整个地方投射指针)