【发布时间】:2020-03-31 05:40:48
【问题描述】:
ABI 标准中与内存布局相关的规范是否通常仅适用于 ABI 边界,或者也适用于例如 ABI 边界。在翻译单元内,或者如果不是这种情况,编译器通常会做出这样的额外保证吗?
如果“一般”过于宽泛,请考虑例如GCC/Clang 与 System V x64 和 Itanium C++ ABI。
这里有两个例子说明我的意思:
-
System V x64 ABI 指定大小至少为 16 字节的数组具有至少 16 字节的对齐,即使元素类型的对齐更小,因此对齐比
alignof建议的更严格。它还指定long double的对齐方式为16。那么如果调用以下在 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; } -
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; }f在 C++ 标准下被调用时具有未定义的行为,因为B和A不是标准布局,因此未指定A子对象是否位于与b相同的地址,如果没有,这会导致std::launder上出现未定义的行为。然而,在 Itanium C++ ABI 下,保证A子对象与b具有相同的地址,因此std::launder将成功。那么在 Itanium C++ ABI 下,即使b从未越过翻译单元边界,这是否安全?
我假设我的两个示例都是安全的,但是这是在引用的标准中还是在编译器的策略中指定的?
【问题讨论】:
-
来自Itanium C++ ABI:“在本文档中,我们为 C++ 程序指定了应用程序二进制接口,即用户 C++ 代码与实现提供的系统和库之间的目标代码接口。” .对我来说,这并不能说明仅在二进制文件中使用的数据对象。因此,“内部”类的内存布局可能不同,但实际上我怀疑任何编译器“内部”和“外部”使用不同的二进制布局。