sizeof 总是一样的吗?
Will sizeof always be the same?
我有一个关于 class 结构化、填充以及由此产生的 sizeof
class 的快速问题。在下面的示例中,在我测试过的每个编译器上,sizeof A
的结果总是 40 个字节,这对我来说很有意义。
#include <iostream>
class A {
int a, b; // 4 + 4
short c; // + 2
double f; // not currently 8 byte aligned. Currently 10 bytes, so pad 6 extra bytes, so: + 6 + 8
char w; // + 1
bool d; // + 1
double g; // not currently 8 byte aligned. Currently 26 bytes, so pad 6 extra bytes, so: + 6 + 8
// 4 + 4 + 2 + 6 + 8 + 1 + 1 + 6 + 8 = 40
};
int main() {
std::cout << sizeof( A );
}
我的问题是,这会永远是真的吗? (假设每个成员的 alignof 和 sizeof 都没有改变)。我知道我可以重新排序它,这样 double
排在第一位,它缩小到 32 个字节;但是编译器可以做出这个决定吗?或者它总是程序员的责任? (我假设编译器无法重新排序)
不允许编译器对成员重新排序,因为它们都具有相同的访问级别。因此,例如,如果有一个 public
成员,那么编译器可以重新排序您的成员变量。
参考:https://timsong-cpp.github.io/cppwp/class.mem#19
C++ 标准不保证您的 class 的大小,但必须保证成员可以单独寻址。
sizeof(A)
在你的程序执行中永远是一样的。如果您更改成员的顺序或 A
的打包和重新编译方式,那么您 can/will 会得到不同的值。如果您使用不同的编译器进行编译,大小也会发生变化。它也可以在使用相同编译器的不同机器上改变,因为不同机器的基本类型可能有不同的大小。
长话短说,如果您的 class 是特定尺寸,则添加一个 static_assert
来检查它。这样,如果它确实发生了变化,您将得到一个很好的编译器错误,而不是可能的 UB。
I know I can reorder it, so that the doubles come first, and it shrinks down to 32 bytes; but can a compiler make this decision?
不,编译器无法重新排序成员以更好地打包,因为您的 class 是标准布局 class 并且所有成员必须按照声明的顺序在内存中布局。
不允许编译器对您的 class 中的属性重新排序(在构造和解构成员时使用此顺序,因此这样做可能会改变程序的含义)。
但是,您也不能指望 sizeof 的结果始终相同。如果出现 64 位 int 比 32 位更有意义的体系结构怎么办?在那种情况下,尺寸会更大。
Will sizeof always be the same?
sizeof 是编译时常量。因此,对于任何已编译的程序,sizeof 不会在两次执行之间发生变化。但是,对于程序的不同编译没有这样的保证。事实上,当程序为另一个系统编译时,即使是基本类型的 sizeof 和 alignof 也是不同的。
I know I can reorder ... can a compiler make this decision?
标准布局 class 有限制。第一个成员保证与超级对象具有相同的地址。此外,对于任何此类 classes 在声明顺序的开头具有相同类型的相同顺序的成员,可以通过单独的 union 成员访问“公共初始序列”,这实际上意味着它们必须具有相同的地址,这有效地防止了成员的任何重新排序,因为编译器无法知道可能存在哪些其他 classes 具有潜在共享的公共初始序列。 A
是标准布局 class.
这些限制不适用于非标准布局 classes,允许编译器更加宽松。据我所知,该语言不会阻止编译器选择非标准布局成员的顺序 classes。但实际上,为了 link 将单独编译的目标文件放在一起,这些目标文件必须符合相同的二进制接口,即它们需要具有相同的类型表示。如果编译器对一个编译使用一种布局,而对另一个编译使用不同的布局,那将破坏 ABI。因此,无论编译器选择什么顺序,它都应该坚持下去。任何打算兼容的编译器都应该使用相同的 ABI。对于 GCC 使用的 Itanium ABI 的价值,指定:
2.4 Non-POD Class Types
II. Allocation of Members Other Than Virtual Bases
For each data component D (first the primary base of C, if any, then the non-primary, non-virtual direct base classes in declaration order, then the non-static data members and unnamed bit-fields in declaration order) ...
Or is it the always the programmer's responsibility? (I'm assuming the compiler can't reorder)
程序员最好假定子对象按声明顺序排列,并以最小化填充的方式选择该顺序。最好不要为了访问非标准布局的内存而假设子对象的顺序 classes.
我有一个关于 class 结构化、填充以及由此产生的 sizeof
class 的快速问题。在下面的示例中,在我测试过的每个编译器上,sizeof A
的结果总是 40 个字节,这对我来说很有意义。
#include <iostream>
class A {
int a, b; // 4 + 4
short c; // + 2
double f; // not currently 8 byte aligned. Currently 10 bytes, so pad 6 extra bytes, so: + 6 + 8
char w; // + 1
bool d; // + 1
double g; // not currently 8 byte aligned. Currently 26 bytes, so pad 6 extra bytes, so: + 6 + 8
// 4 + 4 + 2 + 6 + 8 + 1 + 1 + 6 + 8 = 40
};
int main() {
std::cout << sizeof( A );
}
我的问题是,这会永远是真的吗? (假设每个成员的 alignof 和 sizeof 都没有改变)。我知道我可以重新排序它,这样 double
排在第一位,它缩小到 32 个字节;但是编译器可以做出这个决定吗?或者它总是程序员的责任? (我假设编译器无法重新排序)
不允许编译器对成员重新排序,因为它们都具有相同的访问级别。因此,例如,如果有一个 public
成员,那么编译器可以重新排序您的成员变量。
参考:https://timsong-cpp.github.io/cppwp/class.mem#19
C++ 标准不保证您的 class 的大小,但必须保证成员可以单独寻址。
sizeof(A)
在你的程序执行中永远是一样的。如果您更改成员的顺序或 A
的打包和重新编译方式,那么您 can/will 会得到不同的值。如果您使用不同的编译器进行编译,大小也会发生变化。它也可以在使用相同编译器的不同机器上改变,因为不同机器的基本类型可能有不同的大小。
长话短说,如果您的 class 是特定尺寸,则添加一个 static_assert
来检查它。这样,如果它确实发生了变化,您将得到一个很好的编译器错误,而不是可能的 UB。
I know I can reorder it, so that the doubles come first, and it shrinks down to 32 bytes; but can a compiler make this decision?
不,编译器无法重新排序成员以更好地打包,因为您的 class 是标准布局 class 并且所有成员必须按照声明的顺序在内存中布局。
不允许编译器对您的 class 中的属性重新排序(在构造和解构成员时使用此顺序,因此这样做可能会改变程序的含义)。
但是,您也不能指望 sizeof 的结果始终相同。如果出现 64 位 int 比 32 位更有意义的体系结构怎么办?在那种情况下,尺寸会更大。
Will sizeof always be the same?
sizeof 是编译时常量。因此,对于任何已编译的程序,sizeof 不会在两次执行之间发生变化。但是,对于程序的不同编译没有这样的保证。事实上,当程序为另一个系统编译时,即使是基本类型的 sizeof 和 alignof 也是不同的。
I know I can reorder ... can a compiler make this decision?
标准布局 class 有限制。第一个成员保证与超级对象具有相同的地址。此外,对于任何此类 classes 在声明顺序的开头具有相同类型的相同顺序的成员,可以通过单独的 union 成员访问“公共初始序列”,这实际上意味着它们必须具有相同的地址,这有效地防止了成员的任何重新排序,因为编译器无法知道可能存在哪些其他 classes 具有潜在共享的公共初始序列。 A
是标准布局 class.
这些限制不适用于非标准布局 classes,允许编译器更加宽松。据我所知,该语言不会阻止编译器选择非标准布局成员的顺序 classes。但实际上,为了 link 将单独编译的目标文件放在一起,这些目标文件必须符合相同的二进制接口,即它们需要具有相同的类型表示。如果编译器对一个编译使用一种布局,而对另一个编译使用不同的布局,那将破坏 ABI。因此,无论编译器选择什么顺序,它都应该坚持下去。任何打算兼容的编译器都应该使用相同的 ABI。对于 GCC 使用的 Itanium ABI 的价值,指定:
2.4 Non-POD Class Types
II. Allocation of Members Other Than Virtual Bases
For each data component D (first the primary base of C, if any, then the non-primary, non-virtual direct base classes in declaration order, then the non-static data members and unnamed bit-fields in declaration order) ...
Or is it the always the programmer's responsibility? (I'm assuming the compiler can't reorder)
程序员最好假定子对象按声明顺序排列,并以最小化填充的方式选择该顺序。最好不要为了访问非标准布局的内存而假设子对象的顺序 classes.