Packed struct size is 40 even though member sizes add to 36.

huangapple go评论156阅读模式
英文:

Packed struct size is 40 even though member sizes add to 36

问题

结构体 K 是经过打包的,其所有成员变量的大小之和为 36。每个成员都有一个 static_assert 来检查这一点。

然而,K 本身的大小是 40,而不是 36。K 不是多态的等等。

为什么 K 不是大小为 36?

我使用的是 Clang 16。我不知道一种简单的方法来可视化这个结构体,否则我就会尝试了。如果有人可以推荐一种方法,那将非常棒。

英文:

struct K is packed and the sum of it's class member sizes is 36. Each member has a static_assert checking this.

However, the size of K itself is 40, not 36. K isn't polymorphic etc.

Why is K not size 36?

Using Clang 16. I'm not aware of an easy way to visualize the struct, or I would have. If anyone can recommend a way that would be great.

template<class S>
struct P
{};

struct D : public P<D>
{
    uint64_t x_;
};

enum class E : uint8_t
{};

struct K
{
    D a_;          
    D b_;          
    uint64_t c_;   
    uint64_t d_;   
    uint16_t e_;   
    E f_;          
    E g_;          

    static_assert(sizeof(a_) == 8); // Passes
    static_assert(sizeof(b_) == 8); // Passes
    static_assert(sizeof(c_) == 8); // Passes
    static_assert(sizeof(d_) == 8); // Passes
    static_assert(sizeof(e_) == 2); // Passes
    static_assert(sizeof(f_) == 1); // Passes
    static_assert(sizeof(g_) == 1); // Passes

}__attribute__((packed));

static_assert(sizeof(K) == 36); // Fails, is 40 instead of 36

int main()
{
    K f;
}

答案1

得分: 2

启用了clang的警告后,问题变得明显:

<source>:17:7: 警告:由于其为非POD,因此不将字段'a_'打包为布局目的[-Wpacked-non-pod]
   17 |     D a_;          
      |       ^
<source>:18:7: 警告:由于其为非POD,因此不将字段'b_'打包为布局目的[-Wpacked-non-pod]
   18 |     D b_;          
      |       ^

GCC和clang似乎都没有记录这种行为(请参见GCC packed属性clang packed属性),但看起来使用继承(从P&lt;D&gt;)会禁用packed对成员的影响。
在这种情况下,没有固有的理由为什么D不能被打包,它似乎只是一个普遍的禁令。

如果任何成员未打包,为了确保所有成员的正确对齐,结构需要额外的填充。K的对齐方式是8,如下所示:

static_assert(alignof(K) == 8); // 通过;打包类型的正常对齐方式通常为1

由于K对齐到8字节,并且其成员的组合大小为36,因此必须在末尾添加4个填充字节,以正确对齐K到8字节(将其大小增加到40)。


另请参阅

英文:

With warnings enabled for clang, the issue becomes obvious:

&lt;source&gt;:17:7: warning: not packing field &#39;a_&#39; as it is non-POD
               for the purposes of layout [-Wpacked-non-pod]
   17 |     D a_;          
      |       ^
&lt;source&gt;:18:7: warning: not packing field &#39;b_&#39; as it is non-POD
               for the purposes of layout [-Wpacked-non-pod]
   18 |     D b_;          
      |       ^

Neither GCC nor clang seem to document this behavior (see GCC packed attribute, clang packed attribute), but it looks like using inheritance (from P&lt;D&gt;) disables the effect of packed for a member.
There is no inherent reason why D couldn't be packed in this case, it simply appears to be a general ban.

If any member isn't packed, to ensure proper alignment of all members, the struct requires additional padding. The alignment of K is 8, as evidenced by:

static_assert(alignof(K) == 8); // passes; alignment is normally 1 for packed types

Since K is aligned to 8 bytes, and the combined size of its members is 36, 4 padding bytes have to be added at the end to correctly align K to 8 bytes (by increasing its size to 40).


See Also

huangapple
  • 本文由 发表于 2023年8月11日 00:37:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76877719.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定