如何知道Union使用哪个变量?

时间:2010-11-16 13:17:00

标签: c unions

如果我宣布联盟为:

union TestUnion
{
    struct 
    {
      unsigned int Num;
      unsigned char Name[5];
    }TestStruct;
    unsigned char Total[7];
};

现在,我怎么知道是使用了Total [7]还是使用了TestStruct?

我正在使用C! 我正在重新审视工会和结构,我想到了这个问题。 不能使用“sizeof”,因为两者具有相同的大小,即7个字节。 (还有另一个问题)

当我只填充字符'a'和Tried sizeof(TestUnionInstance)的“Total”时,它返回12(Char的大小为1字节,对吗?)。所以我从中分离出结构,发现结构的大小是12个字节而不是5 + 2 = 7个字节....奇怪!! 任何人都可以解释??

P.S。我正在使用Visual Studio 2008。

6 个答案:

答案 0 :(得分:21)

你做不到。这是工会的一部分。

如果您需要告诉,您可以使用称为标记联合的东西。有些语言对这些语言有内置支持,但在C语言中,你必须自己做。我们的想法是包含一个标记以及union,您可以使用它来分辨它是哪个版本。像:

enum TestUnionTag {NUM_NAME, TOTAL};

struct {
    enum TestUnionTag tag;
    union {
        struct {
            unsigned int Num;
            unsigned char Name[5];
        } TestStruct;
        unsigned char Total[7];
    } value;
} TestUnion;

然后在您的代码中,确保始终设置标记以说明如何使用联合。

关于sizeof:struct是12个字节,因为int有4个字节(大多数现代编译器有4个字节的int,与long int相同),然后是3个字节的填充和5个字节的字符(我不知道填充是在字符之前还是之后)。填充是存在的,因此结构是长整数个字,因此内存中的所有内容在字边界上保持对齐。因为结构长度为12个字节,所以联合必须长12个字节来保存它;联盟不会根据其中的内容改变大小。

答案 1 :(得分:6)

使用的成员是你上次写的那个;其他人是禁区。你知道你上次写信的成员,不是吗?毕竟,是你编写程序的人: - )


至于你的第二个问题:允许编译器在结构中插入“填充字节”,以避免未对齐的访问并使其更具性能

example of a possible distribution of bytes inside your structure

Num    |Name     |pad
- - - -|- - - - -|x x x
0 1 2 3|4 5 6 7 8|9 a b

答案 2 :(得分:4)

简短的回答:除了在你的结构中的某个地方添加枚举之外,没有办法。

enum TestUnionPart
{
  TUP_STRUCT,
  TUP_TOTAL
};

struct TestUnionStruct
{
  enum TestUnionPart Part;
  union
  {
    struct
    {
      unsigned int Num;
      unsigned char Name[5];
    } TestStruct;
    unsigned char Total[7];
  } TestUnion;
};

现在您需要控制联合的创建以确保正确设置枚举,例如使用类似于以下的函数:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts)
{
  tus->Part = TUP_STRUCT;
  memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts));
}

发送正确的值现在只需一个开关:

void print(struct TestUnionStruct const * tus)
{
  switch (tus->Part)
  {
    case TUP_STRUCT:
      printf("Num = %u, Name = %s\n",
             tus->TestUnion.TestStruct.Num,
             tus->TestUnion.TestStruct.Name);
      break;
    case TUP_TOTAL:
      printf("Total = %s\n", tus->TestUnion.Total);
      break;
    default:
      /* Compiler can't make sure you'll never reach this case */
      assert(0);
  }
}

作为旁注,我想提一下,这些结构最好用ML系列的语言处理。

type test_struct = { num: int; name: string }
type test_union = Struct of test_struct | Total of string

答案 3 :(得分:2)

首先,现在大多数架构上的sizeof(int)将是4.如果你想要2,你应该在C99的short标题中查看int16_tstdint.h如果你想具体。

其次,C使用填充字节来确保每个struct与字边界对齐(4)。所以你的结构看起来像这样:

+---+---+---+---+---+---+---+---+---+---+---+---+
|      Num      |   N   a   m   e   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+

最后有3个字节。否则,数组中的下一个struct会在一个位置不对齐的地方放置Num字段,这会降低访问效率。

第三,sizeof联盟将是sizeof最大的成员。即使未使用所有空间,sizeof也将返回最大的结果。

正如其他答案提到的那样,你需要一些其他方式(比如enum)来确定你的联合中使用哪个字段。

答案 4 :(得分:1)

没有办法说出来。你应该有一些额外的标志(或你工会外部的其他方法),说明哪些联合部分真正使用过。

答案 5 :(得分:0)

另一个示例,该示例将联合与枚举包括在一起以确定要存储的内容。我发现它更清楚明确了。

来自: https://www.cs.uic.edu/~jbell/CourseNotes/C_Programming/Structures.html

作者: 约翰·T·贝尔博士


为了知道实际存储哪个联合字段,联合通常嵌套在结构内部,并使用枚举类型指示实际存储在其中的内容。例如:

typedef struct Flight {
    enum { PASSENGER, CARGO } type;
    union {
        int npassengers;
        double tonnages;  // Units are not necessarily tons.
    } cargo;
} Flight;

Flight flights[ 1000 ];

flights[ 42 ].type = PASSENGER;
flights[ 42 ].cargo.npassengers = 150;

flights[ 20 ].type = CARGO;
flights[ 20 ].cargo.tonnages = 356.78;