ABI 规范中的内存布局是否仅适用于 ABI 边界?

wal*_*nut 6 c++ abi

ABI 标准中与内存布局相关的规范是否通常仅适用于 ABI 边界或例如在翻译单元内,或者如果不是这种情况,编译器通常是否会做出此类额外保证?

如果“一般”太宽泛,请考虑例如带有 System V x64 和 Itanium C++ ABI 的 GCC/Clang。

这里有两个例子来说明我的意思:

  1. System V x64 ABI 指定大小至少为 16 字节的数组具有至少 16 字节的对齐,即使元素类型的对齐更小,因此对齐比alignof建议的更严格。它还指定 的对齐方式long double16。那么以下在 C++ 标准下具有未定义行为的函数(如果调用)是否可以安全地在 System V x86 ABI 下使用,即使该storage数组从未跨翻译单元边界公开?

    void f() {
        char storage[16]; // Only guaranteed to have alignment `1` by the C++ standard.
        using T = long double;
        auto p = new(storage) T;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. Itanium C++ ABI 指定了类的布局。例如:

    #include<new>
    
    struct A {
        int i;
        virtual ~A() {}
    };
    
    struct B : A {
        int j;
    };
    
    void f() {
        B b;
        std::launder(reinterpret_cast<A*>(&b))->i = 1;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    f在 C++ 标准下,当被调用时具有未定义的行为,因为BA不是标准布局,因此未指定A子对象是否位于与 相同的地址bstd::launder如果不是,则会导致未定义的行为。然而,在 Itanium C++ ABI 下,可以保证A子对象具有相同的地址b,因此std::launder会成功。那么在 Itanium C++ ABI 下,即使b从未跨越翻译单元边界,这是否安全?

我假设我的两个示例都是安全的,但是这是在参考标准中还是在编译器的策略中指定的?

j6t*_*j6t 0

是的,根据我的阅读,这两种情况都是安全的。

我无法向您指出 Itanium C++ ABI 的某个部分,但您似乎对它必须说的内容很坚定。

但我确实知道:

根据 C++ 标准未定义的行为的一种可能表现是,该语言的某些实现(例如 Itanium C++ ABI)保证了该构造的特定行为。

也就是说,如果一个标准说“未定义”,而另一个标准说“定义为执行 Y”,那么,如果您的实现符合这两个标准,您应该能够假设“Y”发生。

(旁注:另一方面,如果一个标准说“那被定义为执行 X”,而另一个标准说“那被定义为执行 Y”,那么,如果“X”!=“Y”,则您的实现不能符合两个标准。)