我在 g++ 13.2 中得到了奇怪的sizeof()
结果。[[no_unique_address]]
#include <iostream>
using namespace std;
template<bool T>
struct B
{
struct Dummy {};
int *a; // swap position with <below placemark>
// In this position, results are "16" and "40".
[[no_unique_address]] std::conditional_t<T, Dummy, size_t> y;
[[no_unique_address]] std::conditional_t<T, Dummy, size_t> x;
[[no_unique_address]] std::conditional_t<T, Dummy, size_t> h;
[[no_unique_address]] std::conditional_t<T, Dummy, size_t> w;
// <below placemark>
// In this position, results are "8" and "40".
};
int main()
{
cout << sizeof(B<true>) << "\n";
cout << sizeof(B<false>) << "\n";
}
结果是
16
40
但如果你改变int *a;
到线条之后的位置[[no_unique_address]]
,你会得到
8
40
另外,只有一行[[no_unique_address]]
,并且前面有成员指针变量,结果是正确的:
8
16
我阅读了相关文档[[no_unique_address]]
,但没有具体的内容。
为什么会出现这种情况?
令人不满意的答案是,这
[[no_unique_address]]
只是向编译器提示它可能会重叠结构中的成员,它不保证任何特定的布局。(来自标准:“对象大小为零的情况是实现定义的”)。有几种方法可以限制编译器分配字段偏移量的方式。一是场偏移必须是非递减的。另一个是相同类型的字段必须具有不同的偏移量(否则,指向不同字段的指针将无法区分——这对于指向不同类型字段的指针来说不是问题,因为严格的别名规则禁止这些比较)。
这意味着,如果您定义了一个具有 4 个虚拟结构的结构,该结构仍将占用 4 个字节,因为必须为这些字段分配不同的偏移量。
这具体意味着,虽然编译器可以分配
a
和y
相同的偏移量,但它必须分配和x
不同的偏移量。这可以如下所示:h
w
印刷:
显然,编译器决定 的
x
偏移量不得与a
ory
重叠。我不清楚这是编译器的怪癖还是有充分的理由(值得注意的是 GCC 和 Clang 的行为方式相同)。总大小 16 是将大小从 11 字节向上舍入到 8 的倍数的结果。似乎有两种方法可以避免这种情况。一种是将空字段放在前面。另一个是确保它们具有不同的类型:
印刷:
我认为最简单的解决方案是将所有可能为空的字段放在结构的顶部。
T
什么时候true:
sizeof(a)
是 8。sizeof(x)
... 分别等于sizeof(Dummy)
和 1。x
、y
、h
、w
可以相互重叠,它们的总和大小可以为 1 到 4,令其为 1。sizeof(B<true>)
issizeof(a)
+sizeof(x) ...
+ pad(7) 是 8 + 1 + 7 是 16。当你移动
a
到底部时:x
,y
,h
,w
允许与 重叠a
。由于它们是空的,因此它们完全重叠a
。sizeof(B<true>)
重叠(0) +sizeof(a)
是 8。为什么第一种情况与类存储端的零大小没有真正重叠
x
,,,, 。考虑一个数组。member 允许与下一个类成员重叠,但不允许与 重叠。如果是8,就会重叠,这是不允许的。y
h
w
B<true> arr[2]
[[no_unique_address]]
arr[1].a
sizeof(B<true>)
arr[1].a
arr[0].w