【问题标题】:How to know that which variable from Union is Used?如何知道使用了 Union 中的哪个变量?
【发布时间】:2010-11-16 13:17:00
【问题描述】:

如果我将联合声明为:

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

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

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

当我只用字符“a”填充“总计”并尝试sizeof(TestUnionInstance) 时,它返回 12(字符大小为 1 个字节,对吗?)。所以我将结构从中分离出来,发现结构的大小是 12 字节而不是 5+2=7 字节....奇怪! 谁能解释一下??

附:我正在使用 Visual Studio 2008。

【问题讨论】:

    标签: c unions


    【解决方案1】:

    你不能。这是工会的一部分。

    如果您需要能够分辨,您可以使用称为标记联合的东西。有些语言内置了对这些的支持,但在 C 中,你必须自己做。这个想法是在联合中包含一个标签,您可以使用它来判断它是哪个版本。喜欢:

    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 相同),然后是三个字节的填充和五个字节的字符(我不知道填充是在字符之前还是之后)。填充在那里,因此结构是一个整数字长,因此内存中的所有内容都在字边界上保持对齐。因为结构体有 12 个字节长,联合体必须有 12 个字节长才能保存它; union 不会根据里面的内容改变大小。

    【讨论】:

    • 填充出现在char 之后,而不是之前。严格来说,实现可以在它之前放置填充,但是如果你从char [5] 更改为char[6],它也必须在它之前放置相同数量的填充,这将是非常荒谬的。 C 需要具有共同初始元素序列的结构才能兼容。
    【解决方案2】:

    要使用的会员是您上次写信给的会员;其他(S)是禁区。你知道你上次写信给哪个成员,不是吗?毕竟程序是你写的:-)


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

    结构中可能的字节分布示例
    
    数字 |姓名 |pad
    - - - -|- - - - -|x x x
    0 1 2 3|4 5 6 7 8|9 a b
    

    【讨论】:

    • +1 用于花时间显示对象的内存布局。
    • 其实有时候你只需要访问其他成员。联合的一项重要功能是为相同的数据提供不同的视图。
    • 至于“禁区”注释,应该注意的是,就编译器而言,没有什么是禁区。程序员必须手动执行任何此类策略。
    • @thkala - 该标准将此行为定义为未定义,因此除非您明确依赖平台相关行为,否则它是禁止的。在实践中,我知道int is_little_endian(void) { union { int i; char c[sizeof(int)]; } u; u.i = 1; return u.c[0] == 1; } 会做什么,但按照标准,代码是 UB 并且是邪恶的。
    • 这是未定义的 WRT 字节序和结构内存布局。 IS 定义的是,工会的所有“替代方案”都将从同一个地址开始,这使得知道自己在做什么的人可以创造小奇迹:-)
    【解决方案3】:

    简短的回答:除了在联合之外的结构中的某处添加枚举之外,别无他法。

    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
    

    【讨论】:

      【解决方案4】:

      首先,sizeof(int) 在当今大多数体系结构上将是 4。如果您想要 2,您应该查看 short,或者如果您想具体说明,请查看 C99 中 stdint.h 标头中的 int16_t

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

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

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

      第三,sizeof 工会将成为sizeof 它最大的成员。即使没有使用所有空间,sizeof 也会返回最大的结果。

      正如其他答案所提到的,您需要通过其他方式(如enum)来确定使用工会的哪个字段。

      【讨论】:

        【解决方案5】:

        没有办法说。您应该有一些额外的标志(或工会外部的其他方式)来说明真正使用了哪个工会部分。

        【讨论】:

          【解决方案6】:

          另一个使用枚举包含联合以确定存储内容的示例。我发现它更加清晰和重点。

          来自: 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;
          

          【讨论】:

          • 是的!汤姆安德森的回答,被接受的答案,说的完全一样。
          猜你喜欢
          • 2014-12-05
          • 1970-01-01
          • 2016-08-30
          • 2018-05-31
          • 1970-01-01
          • 2020-12-22
          • 1970-01-01
          • 2019-06-04
          • 2013-01-11
          相关资源
          最近更新 更多