C(++) 结构强制额外的填充

C(++) struct force extra padding

本文关键字:填充 结构      更新时间:2023-10-16

我已经看到了无数"我不喜欢填充如何关闭它"形式的问题,但还没有找到任何关于强制编译器提供额外填充的信息。

我的具体案例看起来像

struct particle{
  vect2 s;
  vect2 v;
  int rX;
  int rY;
  double mass;
  int boxNum;
};

其中vect2是一个简单的struct {double x; double y;} vect2. 为了使用 SSE2,我需要能够加载一对与 16 字节边界对齐的双精度。 这曾经有效,直到我添加了额外的int,将我的结构大小从 48 字节推到 56 字节。 结果是段错误。

我可以使用某种编译器指令,要么说"填充此结构以使其成为 16 字节长的倍数",要么说"此结构的对齐方式为 16 字节"? 我知道我可以手动完成(例如,附加一个额外的字符[12]),但我真的宁愿告诉编译器(GCC,最好是 ICC 兼容),而不必手动执行此操作如果我将来更改结构。

gcc 中,你可以将任意类型和变量与 __attribute__((aligned(...))) 对齐。对于您的示例,这将是

struct particle{
  vect2 s;
  vect2 v;
  int rX;
  int rY;
  double mass;
  int boxNum;
} __attribute__((aligned (16)));

这会自动填充结构,以便其数组正确对齐。

您可以嵌套两个结构来自动填充它,而无需自己跟踪大小。

struct particle
{
    // ...
};
{
    particle p;
    char padding[16-(sizeof(particle)%16)];
};

不幸的是,如果结构已经是 16 的倍数,则此版本会增加 16 个字节。这是不可避免的,因为标准不允许零长度的数组。

某些编译器确实允许零长度数组作为扩展,在这种情况下,您可以改为这样做:

struct particle_wrapper
{
    particle p;
    char padding[sizeof(particle)%16 ? 16-(sizeof(particle)%16) : 0];
};

如果结构已经是 16 的倍数,则此版本不会添加任何填充字节。

我正在为此添加自己的答案,以防有人来寻找解决方案。 Mark 的解决方案很简洁,满足了自动要求,但不是我最终选择的解决方案。 我想避免这种情况,这就是我问这个问题的原因,但有一个"微不足道"的解决方案:

struct particle{
  vect2 s;
  vect2 v;
  int rX;
  int rY;
  double mass;
  int boxNum;
  char padding[12];
};

通过手动检查struct的当前大小,您可以添加适当数量的字符(或其他任何内容,但char允许您以字节为单位),以使其大小合适。 这显示了最佳性能和简单性,即使每次结构更改时都需要更新。 在这种情况下,这很好,尽管如果您有一个可以根据选项更改大小的结构,那将是有问题的。

请注意,我的struct是 56 个字节,我添加了 12 个字节,使其成为 64 个字节。 该数学不起作用,因为尾随int已经被填充了 4 个字节到 8 字节边界;struct实际上只有 52 个字节。 仅添加 5 个 char s 就可以工作,方法是将struct长度设置为 57 字节,该长度将填充到 64,但这不是一个不错的解决方案,这就是为什么我使用 12 来使其完全工作。

未测试,但这可能有效:

#include <xmmintrin.h>
struct particle{
  union {
    vect2 s;
    __m128 s_for_alignment;
  };
  union {
    vect2 v;
    __m128 v_for_alignment;
  };
  ...
};

我知道 gcc 以前在正确对齐__m128时遇到问题,但现在应该解决这些问题。

新的 C++11 规范也有一个新功能,尽管我不相信很多供应商已经实现了它们。

您可以尝试使用包编译指示,尽管规范不支持它。 不过,GCC和MS都支持它。

这会在 1 个字节边界上对齐结构,但您可以将数字更改为所需的任何数字。

#pragma pack(push,1)
// ...
#pragma pack(pop)

更新:

因此,显然上述内容将不起作用,因为它只会收缩填充,而不会扩展它。 遗憾的是,今天下午我没有测试环境。

也许使用匿名工会会起作用。 我知道它会扩展到最大的尺寸,尽管我不知道你是否能得到任何关于对齐的保证。

template<typename T, size_t padding_size>
  struct padded_field {
    union {
      T value;
      uint8_t padding[padding_size];
    };
  };