C++ MSVC与clang/gcc上不同的结构字节布局(pragma包行为)

C++ MSVC与clang/gcc上不同的结构字节布局(pragma包行为),c++,windows,clang,pragma,pragma-pack,C++,Windows,Clang,Pragma,Pragma Pack,下面的代码在MSVC和clang/gcc上生成不同的内存布局。为什么? #include <stdio.h> #pragma pack(push,1) struct empty_struct { }; class derived_struct : public empty_struct { int m_derivedMember; }; class derived_struct_container : public empty_struct { public:

下面的代码在MSVC和clang/gcc上生成不同的内存布局。为什么?

#include <stdio.h>

#pragma pack(push,1)

struct empty_struct
{
};

class derived_struct : public empty_struct
{
    int m_derivedMember;
};

class derived_struct_container : public empty_struct
{
public:
    derived_struct  m_ds;
};

class foo
{
public:
    foo() {}
    derived_struct_container m_dsc;
    int m_foo_member;
};
#pragma pack(pop)


int main()
{
    foo fb;
    printf("pf->m_dsc offset: %ld\n", (char *)&fb.m_dsc - (char *)&fb);
    printf("pf->m_dsc.m_ds offset: %ld\n", (char *)&(fb.m_dsc.m_ds) - (char *)&fb);
    printf("pf->m_foo_member offset: %ld\n", (char *)&(fb.m_foo_member) - (char *)&fb);

    return 0;
}
Linux下clang x64上的输出为:

fb.m_dsc offset: 0
fb.m_dsc.m_ds offset: 1
fb.m_foo_member offset: 5
如何使clang布局与MSVC布局匹配?

使用pragma包会导致实现定义的行为

此外,由于具有多个相同类型的基类子对象,foo不是标准布局类,因此即使没有包,其布局也不受任何ABI的约束

坦白地说,依赖非标准布局类的布局是一个糟糕的想法,当然有更好的方法来实现这里的目标

以下是一些不涉及更改代码的可能方法,当然,即使其中任何一种方法目前看来有效,也可能随时更改:

在Windows中使用clang或g++代替MSVC。 尝试向MSVC传递标志以更改EBCO行为,也许可以使其提供0 1 5版本。 编辑gcc或clang的源代码以构建自己的编译器并给出所需的布局。 在gcc中,空基类优化被具有相同类型的两个基类的类禁用,因此您可以通过代码更改来启用它:

其余代码与示例相同。这给了我0 4输出,即使没有pragma包。我不知道gcc或clang的任何标志会改变EBCO的行为

这个规则的原理是,在标准C++中,如果两个相同类型的有效指针具有相同的值,那么它们必须指向同一个对象。这两个空子对象是不同的对象,因此它们必须存在唯一的地址。MSVC在这方面不合格


在C++20中,有一个属性[[no_unique_address]]可以缓解这一需求,但是我在安装g++9.2.0时尝试了它,它没有改变布局。不确定这是一个bug还是预期的行为,但不管怎样,它似乎都不是问题的解决方案

因为这是实现定义的行为。C++标准不需要任何实现的任何特定结果。似乎表明此gcc行为无法更改。clang和gcc都在Windows和Linux上运行,这将有助于在您的问题中澄清这一点,而不是混淆编译器和操作系统。另外,请给出目标的详细信息,因为x64 ABI与32位ABI不同,如果您在windows系统上,您可能会变得非常粗鲁,并向派生的_struct_容器添加一个预处理器指令,该容器会在结构中放置一个字符。也就是说,你没有。我在Windows g++9.2.0 target x86_64-w64-mingw32中得到了输出0 1 5,对于target i686-w64-mingw32也是如此[[no_unique_address]]只能应用于非静态数据成员,但在示例中,没有空类型的非静态数据成员__DeclSpec空库更相关here@RbMm这是MSVC唯一的东西,对吗?我在我的子弹清单中提到了是的,这是你的链接。但是这里的[[no_unique_address]]没有帮助,因为它只影响空类型的非静态数据成员,而不影响继承。需要和一般的C++属性,比如,y-DExStExpEnTyBaseBaseStultE2建议是一个很好的解决了这个问题。如果其中一个空基类也是第一个非静态数据成员的类型或基类,则看起来像。我正在将遗留的Windows MSVC代码库转换为clang,因此这是最好的解决方案。
fb.m_dsc offset: 0
fb.m_dsc.m_ds offset: 1
fb.m_foo_member offset: 5
struct empty_struct {};
struct E2 {};

class derived_struct : public E2