【问题标题】:Size of struct with bit fields in C++ not adding upC ++中位字段的结构大小未加起来
【发布时间】:2021-10-12 03:31:45
【问题描述】:

为什么带有位字段的结构的大小不是我所期望的。

#include <iostream>
using namespace std;

struct test {
    uint8_t x : 4;
    uint16_t y : 8;
    uint16_t z : 8;
};

struct test2 {
    uint8_t x : 4;
    uint16_t y : 10;
    uint16_t z : 10;
};

int main()
{
  cout << sizeof(test) << endl;
  cout << sizeof(test2) << endl;
}

这会打印 4 和 4。 我不明白为什么这两个的大小都不是 3。Test 有 4+8+8 位,即 20,Test2 有 4+10+10 位,即 24 位,都小于或等于 24 位/3 字节。我知道如果在测试中我使用uint_8,它将导致大小为 3,但对于我的实际用例,我需要 Test2(4、10、10 位)。为什么会这样,有没有办法把它变成 3 个字节?

【问题讨论】:

  • 熟悉结构填充,以及编译器调整填充的可用选项,例如#pragma pack 或类似结构。
  • 查找“结构填充”。位域的细节略有不同,但原理是相同的。同样,大多数编译器都支持可以控制填充的选项(例如命令行选项或#pragmas)。禁用(或显式控制)填充有许多影响(例如性能,CPU 可能会触发需要处理以使代码正确运行的硬件异常) - 节省空间并不总是如此(特别是对于现代编译器和平台)通过结构填充是有益的。
  • 如果您使用/Wall 编译,某些编译器会在数据成员后添加填充时显示警告

标签: c++ bit-fields


【解决方案1】:

不仅是位域,而且所有结构都由编译器对齐以获得最大效率。如果您想将它们强制为最小大小,您需要使用 gcc 的属性 packed 或您正在使用的编译器中的等效项,如下所示:

#include <iostream>
using namespace std;

struct test {
    uint8_t x : 4;
    uint16_t y : 8;
    uint16_t z : 8;
} __attribute__ ((packed));

struct test2 {
    uint8_t x : 4;
    uint16_t y : 10;
    uint16_t z : 10;
} __attribute__ ((packed));

int main()
{
  cout << sizeof(test) << endl;
  cout << sizeof(test2) << endl;
}

【讨论】:

  • 所以我认为这样的性能会更差?
  • 是的,字段可能未对齐,需要更多 CPU 周期来解决它们。
【解决方案2】:

Data structure alignment 将帮助您解释输出:https://en.wikipedia.org/wiki/Data_structure_alignment

数据结构增加了更多空间,称为填充。您还需要检查您的编译器,因为不同的编译器对结构对齐有不同的默认选项,并且会有不同的输出,例如,在 VS 中,您可以在此处设置:

此外,您可能还想查看#pragma packhttps://docs.microsoft.com/en-us/cpp/preprocessor/pack?redirectedfrom=MSDN&view=msvc-160

【讨论】:

    【解决方案3】:

    这是为什么

    std::uint16_t 在您的系统上大概有 2 的对齐要求。因此,可以放置成员的第一个有效偏移量是第三个字节(偏移量为 2,可被对齐方式整除)。由于在第二个字节(偏移量 1)处没有可以存储的内容,因此它是无用的填充。

    有没有办法把它变成 3 个字节?

    不,没有将test2 变为 3 个字节的标准方法。对于test,所有成员都可以使用std::uint8_t

    也可能没有必要将其设置为 3 个字节。如果您确实需要精确的位布局,那么对齐不是您唯一关心的问题,因为位域的布局在任何方面都不是标准的。

    【讨论】:

      【解决方案4】:
      #pragma pack( push, 1 )
      
      struct test {
          uint8_t x : 4;
          uint16_t y : 8;
          uint16_t z : 8;
      };
      
      struct test2 {
          uint8_t x : 4;
          uint16_t y : 10;
          uint16_t z : 10;
      };
      
      #pragma pack( pop )
      

      这将按 1 个字节的对齐方式打包结构。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-27
        • 1970-01-01
        • 2016-08-30
        • 2017-06-30
        • 2011-02-08
        相关资源
        最近更新 更多