【问题标题】:size of a structure containing bit fields [duplicate]包含位字段的结构的大小[重复]
【发布时间】:2012-09-27 12:56:17
【问题描述】:

可能重复:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?

我试图理解位域的概念。 但我无法找到为什么 CASE III 中以下结构的大小为 8 个字节。

案例一:

struct B    
{
    unsigned char c;  // +8 bits
} b;

sizeof(b); // 输出:1(因为 unsigned char 在我的系统上占用 1 个字节)

案例二:

struct B
{
    unsigned b: 1;
} b;

 sizeof(b); // Output: 4 (because unsigned takes 4 bytes on my system)

案例三:

struct B
{
    unsigned char c;  // +8 bits
    unsigned b: 1;    // +1 bit
} b;

sizeof(b); // Output: 8 

我不明白为什么案例 III 的输出为 8。我期待 1(char) + 4(unsigned) = 5。

【问题讨论】:

  • 您看到的是结构填充。在这个网站上搜索这些术语,你会发现很多信息
  • unsigned char每个系统上占用一个字节。
  • @CarlNorum: 是的,但这并不意味着带有一个charstruct(例如案例I)也占用一个字节。不同的编译器可以向结构添加三个字节的填充。
  • @MSalters,是的,当然可以。我不确定我是否理解您的意思?

标签: c++ c sizeof bit-fields structure-packing


【解决方案1】:

您可以使用offsetof 来检查结构的布局,但大致如下:

struct B
{
    unsigned char c;  // +8 bits
    unsigned char pad[3]; //padding
    unsigned int bint; //your b:1 will be the first byte of this one
} b;

现在,很明显(在 32 位架构中)sizeof(b) 将是 8,不是吗?

问题是,为什么是 3 个字节的填充,而不是更多或更少?

答案是字段到结构体的偏移量与字段本身的类型具有相同的对齐要求。在你的架构中,整数是 4 字节对齐的,所以 offsetof(b, bint) 必须是 4 的倍数。它不能为 0,因为之前有 c,所以它将是 4。如果字段 bint 从偏移量开始4 并且是 4 字节长,那么结构体的大小是 8。

另一种看待它的方式是,结构的对齐要求是其所有字段中最大的,因此 B 将是 4 字节对齐的(因为它是您的位字段)。但是一个type的大小必须是alignment的倍数,4不够,那就8吧。

【讨论】:

    【解决方案2】:

    我认为您在这里看到了alignment 效果。

    许多架构要求将整数存储在内存中多个字长的地址处。

    这就是为什么你的第三个结构中的 char 被填充了三个字节,因此下面的无符号整数从一个字长的倍数开始。

    【讨论】:

      【解决方案3】:

      Char 根据定义是一个字节。 int 在 32 位系统上是 4 个字节。并且结构被额外填充了 4。

      有关填充的一些解释,请参阅http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

      【讨论】:

      • 在上述情况3中,char是8位(目前没有问题)。我只提供了 1 个无符号整数位。这意味着,剩余的 31 位将被填充。但大小仍应为 --- 8 位(字符)+ 1 位(我提供)+ 31 位(由于填充)== 40 位或 4 字节。为什么它是 8 个字节?
      • 没有办法在任何架构中添加一点。 unsigned int 在您的系统上是 4 个字节
      【解决方案4】:

      为了保持对内存的访问对齐,如果您打包结构,编译器会添加填充,它不会添加填充。

      【讨论】:

        【解决方案5】:

        我又看了一遍,这就是我发现的。

        1. 来自 C 书籍,“关于字段的几乎所有内容都依赖于实现。”
        2. 在我的机器上:
         struct B {
            unsigned c: 8;
            unsigned b: 1;
        }b;
        printf("%lu\n", sizeof(b));
        

        print 4 是一个短的;

        您将位字段与常规结构元素混合在一起。

        顺便说一句,位字段定义为:“单个实现定义的存储单元内的一组相邻 ”所以,我什至不确定 ':8' 做了什么你要。这似乎不符合位域的精神(因为它不再是位域了)

        【讨论】:

          【解决方案6】:

          结构的对齐方式和总大小是特定于平台和编译器的。您不能在这里期待直接且可预测的答案。编译器总能有一些特别的想法。例如:

          struct B
          {
              unsigned b0: 1;    // +1 bit
              unsigned char c;  // +8 bits
              unsigned b1: 1;    // +1 bit
          };
          

          编译器可以将字段 b0 和 b1 合并为一个整数,但不能。这取决于编译器。一些编译器有控制这个的命令行键,一些编译器没有。其他例子:

          struct B
          {
              unsigned short c, d, e;
          };
          

          由编译器来打包/不打包这个结构的字段(假设是 32 位平台)。 DEBUG 和 RELEASE 版本的结构布局可能不同。

          我建议只使用以下模式:

          struct B
          {
              unsigned b0: 1;
              unsigned b1: 7;
              unsigned b2: 2;
          };
          

          当您有一系列共享相同类型的位字段时,编译器会将它们放入一个 int 中。否则,各个方面都会发挥作用。还要考虑到,在一个大项目中,您编写一段代码,而其他人将编写和重写 makefile;将您的代码从一个 dll 移动到另一个。此时编译器标志将被设置和更改。这些人有 99% 的机会不知道您的结构的对齐要求。他们甚至永远不会打开您的文件。

          【讨论】:

          • “编译器可以将字段 b0 和 b1 合并为一个整数,但可能不会” - 是这样吗?我认为编译器需要保持位域的顺序。
          • 我认为最重要的原则是“准确地保留可观察的行为”。除此之外的一切都可能不那么准确,可能因编译器和平台而异,等等。看看黑白世界是不正确的,这正是以这种方式发生的,而以另一种方式发生的其他事情。很多东西都在灰色地带。
          猜你喜欢
          • 2012-08-08
          • 2016-11-21
          • 2011-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多