【问题标题】:How can size of a structure be a non-multiple of 4?结构的大小怎么可能不是 4 的倍数?
【发布时间】:2019-04-09 14:01:12
【问题描述】:

我是结构的新手,正在学习如何计算结构的大小。我知道填充如何发挥作用以正确对齐内存。据我了解,对齐是为了使内存中的大小成为 4 的倍数。 我在 GCC 上尝试了以下代码。

struct books{
  short int number;
  char name[3];
}book;
printf("%lu",sizeof(book));

最初我以为short int 会占用2 个字节,然后是字符数组,从开头的第三个内存位置开始。然后,字符数组需要 3 个字节的填充,其大小为 8。像这样,每个单词代表内存中的一个字节。

short 短字符 char

字符填充填充填充

但是在运行时它的大小为 6,这让我感到困惑。

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 编译器可以随意填充结构成员。在您的情况下,它看起来只关心它是否是最大结构元素的倍数。
  • struct 与其中最大对齐的元素对齐。在这种情况下,short 与 2 个字节对齐,char 仅与 1 个字节对齐,因此结构体与 2 个字节对齐。你有一个 2 字节的short,0 字节的对齐填充,3 字节的char,然后在结构的末尾有 1 个字节的对齐填充,使其成为对齐的倍数。 2+0+3+1=6。
  • 您好,欢迎来到 StackOverflow。好问题。
  • 虽然每个元素都必须根据其大小对齐,但结构的 start 必须与 largest 结构元素对齐。 IE。如果你有uint8_t a; uint16_t b; uint64_t c; uint8_t d;,在a之后会有一个1B的填充,b之后会有7B的填充,c之后没有填充,然后d之后会有7B的填充。当你有一个结构数组时,需要最后的填充来确保c 将对齐。

标签: c struct sizeof


【解决方案1】:

通常,插入填充以允许结构内部元素的对齐访问,而不是让整个结构成为多个单词的大小。对齐是一个编译器实现问题,不是 C 标准的要求。

因此,长度为 3 个字节的 char 元素不需要对齐,因为它们是字节元素。

虽然不是必需的,但最好是短元素需要在短边界上对齐——这意味着一个偶数地址。通过在短边界上对齐它,编译器可以发出一条加载短指令,而不必加载一个字、掩码然后移位。

在这种情况下,填充可能但不一定发生在末尾而不是中间。您必须编写代码来转储元素的地址以确定填充发生的位置。

编辑:。正如@Euguen Sh 所提到的,即使您发现编译器用于结构的填充方案,编译器也可以在不同版本的编译器中对其进行修改。

指望编译器的填充方案是不明智的。总有一些方法可以以您不会猜测对齐方式的方式访问元素。

sizeof() 运算符用于让您查看使用了多少内存,并知道如果该指针增加 1 (ptr++),将向结构中添加多少内存。

编辑 2,打包:可以使用 __packed__ 属性打包结构以防止填充。在设计结构时,使用自然包装的元素是明智的。这在通过通信链路发送数据时尤其重要。精心设计的结构避免了在结构中间进行填充的需要。然后使用__packed__ 属性编译的设计不佳的结构可能具有不自然对齐的内部元素。人们可能会这样做以确保该结构将按照最初设计的方式通过电线传输。随着通过网络传输数据的 JSON 的引入,这种类型的工作已经减少。

【讨论】:

  • 请注意,如果定义此类结构的 array,则填充可能会发生变化。
  • @EugeneSh。不,它不能:数组索引是通过指针偏移量以sizeof (ElementType) 的倍数定义的。如果大小是可变的,那将会中断。换句话说,sizeof (T[N]) == sizeof (T) * N.
  • @KonradRudolph 不,我不是说它可以在同一个版本中改变。我是说编译器可能会根据它的使用情况来决定它如何调整类型的大小(当然是一致的)。我相信我以前见过这个。
  • @EugeneSh.:这不可能发生。首先,如果您在头文件中声明了一个对象,并在数组的一个源文件中使用它,而在另一个源文件中将其用作单个对象,则该对象在两个源文件中必须具有相同的大小。编译器不能给它一个不同的大小,因为它是在一个数组中使用的。其次,sizeof 给出了用于对象的总字节数,包括尾随填充 (C 2018 6.5.3.4 4)。第三,数组是一组连续分配的对象,所以它不能有自己的内部填充;其元素类型的大小必须包括他们需要的填充。
  • @EricPostpischil 我正在尝试重现我相信我之前看到的效果.. 但看起来我做不到。我看到的唯一一件事是__attribute__((aligned(..))) 正在更改结构的大小以适应它作为复合类型的一部分的用例。
【解决方案2】:
#include <stdalign.h>
#include <assert.h>

结构的大小总是可以被成员的最大对齐(必须是 2 的幂)整除。 如果您有一个带有charshort 的结构,则对齐为2,因为short 的对齐为2,如果您有一个结构,则只有在chars 中它的对齐为1。

有多种方法可以控制对齐方式:

alignas(4) char[4]; // this can hold 32-bit ints

这是非标准的,但在大多数编译器(GCC、Clang、...)中都可用:

struct A {
    char a;
    short b;
};

struct __attribute__((packed)) B {
    char a;
    short b;
};

static_assert(sizeof(struct A) == 4);
static_assert(alignof(struct A) == 2);

static_assert(sizeof(struct B) == 3);
static_assert(alignof(struct B) == 1);

【讨论】:

    【解决方案3】:

    通常编译器遵循目标架构的 ABI。 它定义了结构和原始数据类型的对齐方式。这会影响所需的填充和结构大小。因为在许多架构中对齐是 4 的倍数,所以结构的大小也是如此。

    编译器可能会提供一些属性/选项来或多或少地直接更改对齐方式。

    例如 gcc 和 clang 提供:__attribute__ ((packed))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-05
      • 1970-01-01
      • 2015-06-05
      • 2017-11-25
      • 1970-01-01
      相关资源
      最近更新 更多