结构对齐 C/C++

Posted

技术标签:

【中文标题】结构对齐 C/C++【英文标题】:struct alignment C/C++ 【发布时间】:2013-07-24 14:02:41 【问题描述】:

在 c/c++ 中(我假设它们在这方面是相同的),如果我有以下内容:

struct S 
  T a;
  .
  .
  .
 s;

以下内容是否保证为真?

(void*)&s == (void*)&s.a;

或者换句话说,是否有任何保证在第一个成员之前没有填充?

【问题讨论】:

他们在这方面不一样 @MooingDuck:嗯,这取决于... 是什么。对于相同的结构定义,C++ 会以与 C 相同的方式对待它(C 结构始终满足 C++ standard-layout 要求)。 【参考方案1】:

在 C 中,是的,它们是相同的地址。简单明了。


在 C++ 中,不,它们不是同一个地址。基类可以(而且我怀疑,确实)出现在所有成员之前,并且虚拟成员函数通常会在某处将隐藏数据添加到结构中。更令人困惑的是,C++ 编译器还可以随意重新排列成员,除非该类是标准布局类型(尽管我不知道任何编译器都会这样做)

最后,如果 C++ 结构由标准布局类型组成,不包含基类或虚函数,并且所有成员都具有相同的可见性,可能还有其他我忘记的限制,那么 它依赖于 C 规则,并要求第一个成员与对象本身位于同一地址。

第 9.2/7 节

标准布局类是这样的类: — 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员, — 没有虚函数 (10.3) 和虚基类 (10.1), — 对所有非静态数据成员具有相同的访问控制(第 11 条), — 没有非标准布局的基类, — 要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且 — 没有与第一个非静态数据成员相同类型的基类。

第 9.2/20 节

一个指向标准布局结构对象的指针,使用 reinterpret_cast 适当地转换,指向它的初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。 ——尾注]

【讨论】:

“标准布局类”也称为POD(“plain old data type”),可以使用std::is_pod进行验证。 @marcin_j:不。POD 需要 standard-layouttrivial construction/copy/destruction。一个类可以有非默认的构造函数和析构函数以及其他特殊成员,并且仍然是标准布局 感谢您的澄清,我看到可以使用 std::is_standard_layout 来验证这种比较是否正确。 “基类可以(我怀疑,确实)出现在所有成员之前”——基类在布局中的位置是未定义的;一些编译器(没有一个“主流”编译器)确实将它们放在类 iirc 的末尾。 @SeanMiddleditch:我知道它是 IB,但我听说大多数编译器将 nonvirtual 基类放在成员之前,我听说过关于放置虚拟基类。【参考方案2】:

是的。

保证在 C 和 C++ 中的第一个结构成员之前没有填充(如果它是 POD)。

C 引用:

(C11, 6.7.2.1p15) "结构对象中可能有未命名的填充,但不是在其开头。"

C++ 引用:

(C++11, 9.2p20) “因此,标准布局结构对象中可能有未命名的填充,但不是在其开头,这是实现适当对齐所必需的”

【讨论】:

在 C++ 中,只有当它是 POD 时才能保证。 @n.m.添加了对 C++ 的引用和对 POD 的提及。谢谢 @n.m.如果什么是 POD?结构体S 或成员T? 比较 &s == &s.a 是非法的,因为 struct S*T* 不是兼容的类型。但是(void*)&s == (void*)&s.a @huseyintugrulbuyukisik 如果您的意思是在运行时移动?那就不要。 编译器可以重新排列它们,但运行时不会。没错,它不能,因为它不知道对象的位置。

以上是关于结构对齐 C/C++的主要内容,如果未能解决你的问题,请参考以下文章

确定 C/C++ 结构中元素对齐的算法

C语言 | 结构体内存对齐

结构体内存对齐——

1分钟了解C语言正确使用字节对齐及#pragma pack的方法

C/C++ struct/class/union内存对齐

转C/C++ struct/class/union内存对齐