【问题标题】:Misalignment of members in structures [duplicate]结构中的成员错位[重复]
【发布时间】:2014-05-14 20:10:53
【问题描述】:

在 C 中,有时结构的某些成员往往具有未对齐的偏移量,例如 HPUX community 中的这个线程

在这种情况下,建议使用零宽度位字段来对齐(未对齐)下一个成员。 什么情况下会发生结构件错位?在字边界对齐成员的偏移量不是编译器的工作吗?

【问题讨论】:

  • 请引用您的来源。 (“建议一个”?由谁?)。编译器的工作是根据需要正确对齐成员,并且编译器做得很好。所以没有偏移偏移的趋势。
  • @rici 好吧,我正在跟进HPUX community 中的一个问题,该问题是由于结构的成员未对齐而生成的核心转储。你可以看到它确实发生了,而且人们确实被它所困扰。
  • @ZanLynx,我在发帖前确实看过那个帖子,但它没有回答我关于为什么编译器不只对齐结构的某些成员的问题。
  • 编译器自动为结构成员提供他们需要的任何对齐方式。您引用的线程中的代码可能错误地假设比成员实际要求的对齐更严格。
  • @Phalgun:请编辑您的问题以包含该链接。否则非常很难知道你在说什么。

标签: c structure bit-fields


【解决方案1】:

只有当结构成员的对齐要求被故意隐藏时,才会发生结构成员的“未对齐”。 (或者如果使用某些特定于实现的机制来抑制对齐,例如 gcc 的 packed 属性。)

例如,在引用的问题中,问题是有一个结构:

struct {
    // ... stuff
    int               val;
    unsigned char     data[DATA_SIZE];
    // ... more stuff
}

并且程序员尝试使用data,就好像它是size_t

*(size_t*)s->data

但是,程序员已将data 声明为unsigned char,因此编译器仅保证它被对齐以用作unsigned char

碰巧data 跟在int 之后,因此也与int 对齐。在某些架构上这可行,但在目标架构上,size_tint 大,需要更严格的对齐。

很明显,编译器无法知道您打算使用结构成员,就好像它是其他类型一样。如果您这样做并针对需要正确对齐的架构进行编译,您可能会遇到问题。

引用的线程建议在声明unsigned char 数组之前插入一个长度为零的size_t 位域,以强制数组与size_t 对齐。虽然该解决方案可能适用于目标体系结构,但它是不可移植的,不应在可移植代码中使用。不能保证 0 长度的位字段将占用 0 位,也不能保证基于 size_t 的位字段实际上将存储在 size_t 中或针对任何非位字段进行适当对齐现场使用。

更好的解决方案是使用匿名联合:

// ...
int             val;
union {
  size_t        dummy;
  unsigned char data[DATA_SIZE];
};
// ...

使用 C11,您可以明确指定最小对齐方式:

// ...
int                            val;
_Alignas(size_t) unsigned char data[DATA_SIZE];
// ...

在这种情况下,如果您 #include <stdalign.h>,您可以拼写 _Alignas,其方式也适用于 C++11:

int                            val;
alignas(size_t) unsigned char data[DATA_SIZE];

【讨论】:

  • 感谢您的解释@rici。但是,我想添加一段 C99 标准的摘录,“作为一种特殊情况,宽度为 0 的位域结构成员表示不再将位域打包到包含前一个位的单元中——字段,如果有的话,被放置”这不是表示零宽度位字段应该是可移植的吗?
  • @Phalgun:这句话说的正是它所说的,仅此而已。阅读前面的段落:“实现可以分配任何大到足以容纳位域的可寻址存储单元......可寻址存储单元的对齐方式是未指定的。”所以不能保证对齐或大小;只是由零长度位域分隔的两个位域不在同一个可寻址存储单元中。这不会影响后续char 数组的对齐方式,因为没有后续位域,即使有,也不会定义对齐方式。
【解决方案2】:

问:为什么会发生错位?在字边界对齐成员的偏移量不是编译器的工作吗?

您可能知道结构字段与特定边界对齐的原因是为了提高性能。正确对齐的字段可能只需要 CPU 进行一次内存提取操作;其中未对齐的字段将需要至少两次内存提取操作(CPU 时间的两倍)。

正如您所指出的,对齐结构字段以实现最快的 CPU 访问是编译器的工作;除非程序员重写了编译器的默认行为。

那么问题可能是;为什么程序员会覆盖编译器的默认结构字段对齐方式?

程序员想要覆盖默认对齐方式的一个例子是在将结构“通过网络”发送到另一台计算机时。通常,程序员希望将尽可能多的数据打包成最少的字节数。

因此,当结构密度比访问结构字段的 CPU 性能更重要时,程序员将禁用默认对齐方式。

【讨论】:

  • 值得注意的是,并非所有 CPU 只会遭受性能下降。一些,例如 Sparc 将核心转储在非对齐访问。
  • @camelccc,感谢您的澄清。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-01
  • 2013-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
相关资源
最近更新 更多