4
struct abc
{
  char arr[7];
  char arr1[2];
  int i:24;
};

在上面使用 sizeof 运算符的结构中,我得到了它的大小 12 字节。但根据我的计算(可能是错误的)它应该是 16 字节。为什么它给出 12 个字节?

另一个问题:

根据 C99 第 6.7.2.1 节第 14 段

结构或联合对象的每个非位字段成员都以适合其类型的实现定义的方式对齐。

对于特定的实现,我从哪里可以获得描述特定编译器(例如 gcc)如何在结构中引入填充的文档?对于特定架构的所有编译器是否有任何通用规则?

4

2 回答 2

2

结构布局是实现定义的。事实证明,例如,GCC 使用的默认布局与 MSVC 使用的布局不同。我猜您已经习惯了 MSVC 布置包含位域的结构的方式。

当然,还有一个 GCC 属性,ms_struct允许您更改行为。这在文档中有更详细的描述。

所以,这个结构的大小为 16:

struct abc
{
  char arr[7];
  char arr1[2];
  int i:24;
} __attribute__((ms_struct));

如果您使用该gcc_struct选项(默认值),则大小为 12。

对于特定的实现,我从哪里可以获得描述特定编译器(例如 GCC)如何在结构中引入填充的文档?

您需要查阅每个编译器的文档。在 GCC 的情况下,文档说

4.9 结构、联合、枚举和位域

  • 使用不同类型的成员访问联合对象的成员 (C90 6.3.2.3)。

    对象表示的相关字节被视为用于访问的类型的对象。请参阅类型双关语。这可能是一个陷阱表示。

  • 将“普通” int 位域视为有符号 int 位域还是无符号 int 位域(C90 6.5.2、C90 6.5.2.1、C99 6.7.2、C99 6.7.2.1)。

    默认情况下,它被视为有符号整数,但这可以通过 -funsigned-bitfields 选项进行更改。

  • _Bool、signed int 和 unsigned int (C99 6.7.2.1) 以外的允许位域类型。

    在严格符合模式下不允许使用其他类型。

  • 位域是否可以跨越存储单元边界(C90 6.5.2.1,C99 6.7.2.1)。

    由 ABI 确定。

  • 一个单元内位域的分配顺序(C90 6.5.2.1,C99 6.7.2.1)。

    由 ABI 确定。

  • 结构的非位域成员的对齐(C90 6.5.2.1,C99 6.7.2.1)。

    由 ABI 确定。

  • 与每个枚举类型兼容的整数类型(C90 6.5.2.2、C99 6.7.2.2)。

    通常,如果枚举中没有负值,则类型为 unsigned int,否则为 int。如果指定了-fshort-enums,那么如果有负值,它是可以表示所有值的signed char、short和int中的第一个,否则它是unsigned char、unsigned short和unsigned int中第一个可以表示所有值的价值。

    在某些目标上,-fshort-enums 是默认值;这是由 ABI 决定的。

所以,总的来说,你需要弄清楚你的平台的 ABI 是什么。对于任何编译器来说,这确实是明智之举。如果它不根据 ABI 布局结构,那么它会使互操作变得非常棘手。

有点奇怪的是,GCC 对 Windows 上的 ABI 的看法与 MSVC 实现不同。我不知道为什么会这样。

于 2013-04-14T11:24:18.300 回答
1

这很简单。

struct abc
{
  char arr[7];  // occupies 7 bytes
  char arr1[2]; // occupies 2 bytes
  int i:24;     // occupies 3 bytes
};

现在,在(的i)的第三个声明中,只需要 3 个字节。您已经拥有如下:

0 1 2 3 // All 4 bytes used for `char arr[7]`
0 1 2 3 // 3 more used for `char arr[7]`, 1 used for `char arr1[2]`
0 1 2 3 // 1 used for `char arr1[2]`, and the remaining 3 bytes will be used for `int i:24`

但是如果你使用int i(无位域),它会消耗 16 个字节,因为

0 1 2 3 // All 4 bytes used for `char arr[7]`
0 1 2 3 // 3 more used for `char arr[7]`, 1 used for `char arr1[2]`
0 1 2 3 // 1 used for `char arr1[2]`, there are still 3 bytes but we need 4 bytes for an `int`
0 1 2 3 // So the compiler will allocate a new 4 byte chunk for `int i`

我想现在已经很清楚了。

于 2013-04-14T11:14:28.120 回答