C中的联盟内存份额

时间:2011-01-12 15:22:35

标签: c unions

Edit2:我可以用Union做多态吗?在我看来,我可以根据自己的需要更改数据结构。

编辑:修复代码。使用 ”。”而不是“ - >”。我想问的是,如果有不同的数据类型(如int和char可以互换使用,如何确保正确存储值?由于两者具有不同的内存大小,需要更大内存空间的那个将为两者分配内存空间要分享的变量类型。

假设我有两个结构:

typedef struct a{
          int a;
}aType;

typedef struct b{
          char b;
}bType;

typedef union{
         aType a_type;
         bType b_type;
}ab;

int main(void){
         ab v1;
         v1.a_type.a = 5;
         v1.b_type.b = 'a'
}

据我所知,aType和bType都将共享相同的内存。由于int有3个字节更大(int是4个字节,char是1个字节),因此它将有4个内存块。第一个是最左边,最后一个是最右边。我将'a'分配给v1的变量b的时间,它将保留在内存块的第一个块(最左边)。值5仍然保留在第四块内存中(最右侧)。

因此,当打印出来时,会产生垃圾值,不是吗?如果是这样,如何解决这个问题?通过这个问题,这意味着如果我将'a'存储到b_type中,共享内存必须确保仅具有该值'a',而不是之前的整数值5。

5 个答案:

答案 0 :(得分:7)

没有正确的行为。通过一个成员设置union并从另一个成员检索值会导致未定义的行为。您可以使用此技术执行有用的操作,但它依赖于硬件和编译器。您需要考虑处理器字节顺序和内存对齐要求。

当我几乎用C编写所有编程时,有两种(便携式)技术使用了我非常依赖的联合。

标记的联合。当您需要动态类型变量时,这很好。您设置了一个包含两个字段的结构:类型判别式和所有可能类型的并集。

struct variant {
  enum { INT, CHAR, FLOAT } type;
  union value {
    int i;
    char c;
    float f;
  };
};

只要更改了union的值并且只检索该类型指定的值,就必须非常小心地正确设置类型值。

通用指针。由于您可以非常确定所有指针具有相同的大小和表示形式,因此您可以创建指针类型的并集,并且知道您可以交替设置和检索值,而不考虑类型:

typedef union {
  void *v;
  int* i;
  char* c;
  float* f;
} ptr;

这对于(反)序列化二进制数据特别有用:

// serialize
ptr *p;
p.v = ...; // set output buffer
*p.c++ = 'a';
*p.i++ = 12345;
*p.f++ = 3.14159;

// deserialize
ptr *p;
p.v = ...; // set input buffer
char c = *p.c++;
int i = *p.i++;
float f = *p.f++;

仅供参考:您可以让您的示例更简单。结构是不必要的。你会得到同样的行为:

int main() {

  union {
    int a;
    char b;
  } v1;

  v1.a = 5;
  v1.b = 'a';
}

答案 1 :(得分:1)

您描述的行为取决于平台/系统/编译器。例如,在Intel x86处理器上,5可能是int编译器gcc中的第一个字节。

union兴趣来自两个主要角度

  • 共享相同的内存空间,以便最小化所需的内存分配(在这种情况下,第一个字节[例如]可能指示结构/联合中的数据类型。)
  • 分析一些数据结构,无需使用强制转换和指针。例如,在某些平台上doublechar[8]之间的联合是获取double结构的每个字符/字节视图的简单方法。

如果使用union没有任何好处,请不要这样做。

答案 2 :(得分:0)

解决此问题的唯一方法是跟踪存储的数据。这通常使用所谓的标记成员来完成,如下所示:

struct mystructA {
    int data;
};
struct mystructB {
    char data;
};
enum data_tag {
    TAG_STRUCT_A,
    TAG_STRUCT_B
};
struct combined {
    enum data_tag tag;
    union {
        struct mystructA value_a;
        struct mystructA value_b;
    } data;
};

通过仔细跟踪您输入的数据,您可以确保稍后只读取相同的字段,从而确保您获得有意义的结果。

答案 3 :(得分:0)

如果您通过上次分配给它的同一元素访问union,则没有问题。通过访问union的char大小元素,编译器确保只返回您感兴趣的位。

编辑:人们提到了标记的工会。这是另一种风格,SDL将其用于事件结构。

enum union_tag {
    STRUCT_A,
    STRUCT_B
};

typedef struct {
    enum union_tag tag;
    int a;
} aType;

typedef struct {
    enum union_tag tag;
    char b;
} bType;

typedef union{
    enum union_tag tag;
    aType a_type;
    bType b_type;
} ab;

要访问元素,您可以执行以下操作:

int result;

switch(my_union.tag){
    case STRUCT_A:
         result = my_union.a_type.a;
         break;
    case STRUCT_B:
         result = my_union.b_type.b;
         break;
}

答案 4 :(得分:0)

好吧,首先我们应该知道你是否正在使用Big Endian od Little Endian处理器。 Windows& Linux使用小端格式,这意味着值0x00000005实际上写为05-00-00-00,就好像你从右到左书写。
所以,第一,你把5放入一个部分,这意味着第一个字节是05,其他所有都是00。 比你将'a'放入b部分,用相应的ascii值覆盖05,即0x61。 当你看到结果数字应该是...... 97,那就是0x61的值。

union的对齐应该从头开始,但字节顺序依赖于平台。 你告诉我们在Big Endian体系结构下应该像Sun Solaris或任何Risc处理器一样正确。

我错了吗?

HTH