【问题标题】:Why is this UNION like a struct?为什么这个 UNION 像一个结构?
【发布时间】:2021-11-08 19:25:28
【问题描述】:

我遇到了一些具有联合的代码(它位于 Microchip 的库中)。

一切都很好,直到我看到它为工会的不同成员分配了价值一个接着一个。我的直接想法是“他们在写同一个位置......” 然后我决定做一个测试。根据我认为我理解的每一项措施,这个联合应该是一个字节(8 位)。但它不是……它是 4 个字节。

#pragma pack(1)
typedef union _STATUS
{
    BYTE Val;
    struct {
        unsigned BC8 : 1;
        unsigned BC9 : 1;
        unsigned BSTALL : 1;
        unsigned DTSEN : 1; 
        unsigned INCDIS : 1;
        unsigned KEN : 1;   
        unsigned DTS : 1;   
        unsigned UOWN : 1;  
    };
    struct {
        unsigned BC8 : 1;
        unsigned BC9 : 1;
        unsigned PID0 : 1;
        unsigned PID1 : 1;
        unsigned PID2 : 1;
        unsigned PID3 : 1;
        unsigned : 1;
        unsigned UOWN : 1;
    };
    struct {
        unsigned : 2;
        unsigned PID : 4; 
        unsigned : 2;
    };
} STATUS;
void PrintIt() {
    printf("Size of UNION is %d \n", sizeof(STATUS));
}

它应该是所有成员中最大的,每个成员只有8位。

引起我注意并让我对此进行调查的代码是:

STAT.BC9 = 0;
STAT.BC8 = 0;
STAT.Val |= byteToSend;

第三行合并到第一行和第二行的值。

所以我想测试它,它是 4 个字节,而不是一个。我什至在几个不同的编译器中对其进行了测试(因此#pragma 用于 MS Visual C)。

每个成员正好是 8 位,最后两个结构重叠以将 PID 值放在相同的内存位置。然而,我使用编译器评估它的每种方式都是 4 个字节。

向联合中添加结构的行为有什么问题吗?

感谢任何解释。

【问题讨论】:

  • union _STATUS sigh...你会认为到 2021 年,人们终于习惯了typedef union NAME { ... } NAME; - 这是完全合法、方便且不'不需要踩保留标识符。
  • 相当肯定在联合的匿名结构中定义了两次相同的成员是 UB...
  • @StoryTeller-UnslanderMonica 不是我的代码。问题是关于存储大小,而不是声明语法。
  • 这就是我发表评论而不是答案的原因
  • 您的编译器不会抱怨BC8BC9UOWN 的重新定义。 GCC、Clang 和 MSVC 会报错,那么你是如何在“几种不同的编译器”中测试这段代码的呢?

标签: c struct unions


【解决方案1】:

虽然未在 C 标准中明确指定,但位域通常会占用与声明它们的基本类型相对应的单元。

在这种情况下,所有位域都声明为unsigned。这种类型在您的系统上可能是 4 个字节,因此位域占用该类型的一个单元。

如果您将字段类型更改为unsigned charuint8_t,它们应该只占用一个字节。请注意,这假设您的编译器允许将这些类型用于位域,尽管大多数都允许。

【讨论】:

    【解决方案2】:

    C 有隐式类型的概念。所以unsigned 将声明一个unsigned int。但它变得更奇怪了。如果您只使用conststatic/auto,情况也是如此。

    const x = 5; // Declares a const int variable
    static x;    // Declares a static int variable
    const static unsigned x = 5; // Declares a const static unsigned int variable
    

    你想要的是unsigned char

    它应该是所有成员中最大的,每个成员只有8位。

    它应该是至少那个大小。没有什么能阻止它变大。

    【讨论】:

      【解决方案3】:
      1. 由于匿名结构中有重复的成员名称,因此无法编译。
      2. 如果您的位域使用 8 位类型,则确实需要打包。
      typedef union 
      {
          unsigned char Val;
          struct {
              unsigned char BC8 : 1;
              unsigned char BC9 : 1;
              unsigned char BSTALL : 1;
              unsigned char DTSEN : 1; 
              unsigned char INCDIS : 1;
              unsigned char KEN : 1;   
              unsigned char DTS : 1;   
              unsigned char UOWN : 1;  
          };
          struct {
              unsigned char BC81 : 1;
              unsigned char BC91 : 1;
              unsigned char PID0 : 1;
              unsigned char PID1 : 1;
              unsigned char PID2 : 1;
              unsigned char PID3 : 1;
              unsigned char : 1;
              unsigned char UOWN1 : 1;
          };
          struct {
              unsigned char : 2;
              unsigned char PID : 4; 
              unsigned char : 2;
          };
      } STATUS;
      
      int main(void) {
          printf("Size of UNION is %d \n", (int)sizeof(STATUS));
      }
      

      https://godbolt.org/z/zsfhnGeWd

      请记住,有些编译器会打包成您指定的类型(例如 chibicc)。

      【讨论】:

        【解决方案4】:

        在 dbush 回答的同时,我也在深入挖掘之后添加了自己的答案。

        我将其描述为: “无符号”意味着一个“int”所以你定义了一个整数存储的前 8 位。

        typedef union _STATUS {
        BYTE Val;
        struct {
            unsigned char BC8 : 1;
            unsigned char BC9 : 1;
            unsigned char BSTALL : 1;
            unsigned char DTSEN : 1; 
            unsigned char INCDIS : 1;
        ....
        } STATUS;
        

        当限制大小为无符号字符时,联合现在实际上是 1 个字节。

        【讨论】:

        • 与往常一样,输入问题会让您思考更多,看得更远。我会把它记下来,除非它可能会帮助有类似问题的其他人。
        • 您的问题得到了投票,并且您已经得到了答案,所以我认为其他人从仍然可见的问题中受益。你也可以接受你得到的答案之一。
        • @TedLyngmo 谢谢。在撰写我自己的答案时,我是第一个支持 dbush 答案的人。
        猜你喜欢
        • 2013-11-19
        • 1970-01-01
        • 1970-01-01
        • 2021-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-25
        • 2021-04-08
        相关资源
        最近更新 更多