当int64_t更改为int32_t时,为什么类大小会增加

时间:2016-07-12 17:54:07

标签: c++ memory-alignment bit-fields memory-layout object-layout

在我的第一个例子中,我使用int64_t有两个位域。当我编译并获得类的大小时,我得到8。

class Test
{
    int64_t first : 40;
    int64_t second : 24;
};

int main()
{
    std::cout << sizeof(Test); // 8
}

但当我将第二个bitfeild更改为int32_t时,该类的大小增加到16:

class Test
{
    int64_t first : 40;
    int32_t second : 24;
};

int main()
{
    std::cout << sizeof(Test); // 16
}

在GCC 5.3.0和MSVC 2015上都会发生这种情况。但为什么呢?

4 个答案:

答案 0 :(得分:37)

在你的第一个例子中

int64_t first : 40;
int64_t second : 24;

firstsecond都使用64位整数的64位。这会导致类的大小为单个64位整数。在第二个例子中你有

int64_t first : 40;
int32_t second : 24;

这是两个独立的位字段存储在两个不同的内存块中。您使用64位整数的40位,然后使用24位另一个32位整数。这意味着您至少需要12个字节(此示例使用8位字节)。很可能你看到的额外4个字节是填充以在64位边界上对齐类。

正如其他答案和评论所指出的那样,这是实现定义的行为,您可以/将在不同的实现上看到不同的结果。

答案 1 :(得分:15)

C标准的位域规则不够精确,无法告诉程序员任何有关布局的有用信息,但仍然拒绝实现本来可能有用的自由。

特别是,需要将位域存储在具有指示类型的对象中的,或其签名/无符号等效物。在第一个示例中,第一个位域必须存储在int64_t或uint64_t对象中, 而第二个同样,但有足够的空间让他们适应 同一个对象。在第二个例子中,第一个位域必须存储在一个 int64_t或uint64_t,以及int32_t或uint32_t中的第二个。 uint64_t将有24位,这将是&#34;搁浅&#34;即使在结构的末尾添加了额外的位字段; uint32_t有8位目前尚未使用,但可以使用另一个宽度小于8的int32_t或uint32_t位域添加到该类型中。

恕我直言,该标准抨击了给予编译器自由与给予程序员有用信息/控制之间最糟糕的平衡,但它就是它的本质。我个人认为如果首选语法允许程序员根据普通对象精确地指定其布局(例如,bitfield&#34; foo&#34;应该以3位存储,从第4位开始(值为16),我认为位域会更有用),of field&#34; foo_bar&#34;)但我知道没有计划在标准中定义这样的东西。

答案 2 :(得分:6)

添加其他人已经说过的内容:

如果要检查它,可以使用编译器选项或外部程序输出结构布局。

考虑这个文件:

// test.cpp
#include <cstdint>

class Test_1 {
    int64_t first  : 40;
    int64_t second : 24;
};

class Test_2 {
    int64_t first  : 40;
    int32_t second : 24;
};

// Dummy instances to force Clang to output layout.
Test_1 t1;
Test_2 t2;

如果我们使用布局输出标志,例如Visual Studio的/d1reportSingleClassLayoutX(其中X是类或结构名称的全部或部分)或Clang ++的-Xclang -fdump-record-layouts(其中{{1}告诉编译器将-Xclang解释为Clang前端命令而不是GCC前端命令),我们可以将-fdump-record-layoutsTest_1的内存布局转储到标准输出。 [不幸的是,我不确定如何直接使用GCC。]

如果我们这样做,编译器将输出以下布局:

  • Visual Studio:
Test_2
  • 锵:
cl /c /d1reportSingleClassLayoutTest test.cpp

// Output:
tst.cpp
class Test_1    size(8):
    +---
 0. | first (bitstart=0,nbits=40)
 0. | second (bitstart=40,nbits=24)
    +---



class Test_2    size(16):
    +---
 0. | first (bitstart=0,nbits=40)
 8. | second (bitstart=0,nbits=24)
    | <alignment member> (size=4)
    +---

请注意,用于生成此输出的Clang I版本(Rextester使用的版本)似乎默认将两个位域优化为单个变量,并且我不确定如何禁用此行为。< / p>

答案 3 :(得分:5)

标准说:

  

§9.6位字段

     

在类中分配位字段   对象是实现定义的。位字段的对齐是实现定义的。 [注意:比特字段跨越某些机器上的分配单元而不是   在别人身上。在某些机器上从右到左分配位字段,在其他机器上从左到右分配。 - 结束记录]

c++11 paper

因此布局取决于编译器实现,编译标志,目标拱等。刚检查了几个编译器,输出主要是8 8

#include <stdint.h>
#include <iostream>

class Test32
{
    int64_t first : 40;
    int32_t second : 24;
};

class Test64
{
    int64_t first : 40;
    int64_t second : 24;
};

int main()
{
    std::cout << sizeof(Test32) << " " << sizeof(Test64);
}