[英]Why does an uint64_t needs more memory than 2 uint32_t's when used in a class? And how to prevent this?
I have made the following code as an example.我以下面的代码为例。
#include <iostream>
struct class1
{
uint8_t a;
uint8_t b;
uint16_t c;
uint32_t d;
uint32_t e;
uint32_t f;
uint32_t g;
};
struct class2
{
uint8_t a;
uint8_t b;
uint16_t c;
uint32_t d;
uint32_t e;
uint64_t f;
};
int main(){
std::cout << sizeof(class1) << std::endl;
std::cout << sizeof(class2) << std::endl;
std::cout << sizeof(uint64_t) << std::endl;
std::cout << sizeof(uint32_t) << std::endl;
}
prints印刷
20
24
8
4
So it's fairly simple to see that one uint64_t is as large as two uint32_t's, Why would class 2 have 4 extra bytes, if they are the same except for the substitution of two uint32_t's for an uint64_t.所以很容易看出一个 uint64_t 和两个 uint32_t 一样大,为什么类 2 会有 4 个额外的字节,如果它们是相同的,除了用两个 uint32_t 替换一个 uint64_t 之外。
As it was pointed out, this is due to padding .正如有人指出的那样,这是由于padding 。
To prevent this, you may use为了防止这种情况,您可以使用
#pragma pack(1)
class ... {
};
#pragma pack(pop)
It tells your compiler to align not to 8 bytes, but to one byte.它告诉您的编译器不要对齐 8 个字节,而是对齐 1 个字节。 The pop command switches it off (this is very important, since if you do that in the header and somebody includes your header, very weird errors may occur) pop 命令将其关闭(这非常重要,因为如果您在标题中执行此操作并且有人包含您的标题,则可能会发生非常奇怪的错误)
The rule for alignment (on x86 and x86_64) is generally to align a variable on it's size .对齐规则(在 x86 和 x86_64 上)通常是对齐变量的 size 。
In other words, 32-bit variables are aligned on 4 bytes, 64-bit variables on 8 bytes, etc.换句话说,32 位变量在 4 个字节上对齐,64 位变量在 8 个字节上对齐,等等。
The offset of f
is 12, so in case of uint32_t f
no padding is needed, but when f
is an uint64_t
, 4 bytes of padding are added to get f
to align on 8 bytes. f
的偏移量为 12,因此在uint32_t f
情况下不需要填充,但是当f
是uint64_t
,添加 4 个字节的填充以使f
对齐 8 个字节。
For this reason it is better to order data members from largest to smallest .因此,最好将数据成员从大到小排序。 Then there wouldn't be any need for padding or packing (except possibly at the end of the structure).那么就不需要填充或包装(除非可能在结构的末尾)。
Why does an uint64_t needs more memory than 2 uint32_t's when used in a class?为什么在类中使用时 uint64_t 比 2 个 uint32_t 需要更多内存?
The reason is padding due to alignment requirements.原因是由于对齐要求而填充。
On most 64-bit architectures uint8_t has an alignment requirement of 1, uint16_t has an alignment requirement of 2, uint32_t has an alignment requirement of 4 and uint64_t has an alignment requirement of 8. The compiler must ensure that all members in a structure are correctly aligned and that the size of a structure is a multiple of it's overall alignment requirement.在大多数 64 位架构上,uint8_t 的对齐要求为 1,uint16_t 的对齐要求为 2,uint32_t 的对齐要求为 4,uint64_t 的对齐要求为 8。编译器必须确保结构中的所有成员都正确对齐并且结构的大小是其整体对齐要求的倍数。 Furthermore the compiler is not allowed to re-order members.此外,编译器不允许重新排序成员。
So your structs end up laid out as follows所以你的结构最终布局如下
struct class1
{
uint8_t a; //offset 0
uint8_t b; //offset 1
uint16_t c; //offset 2
uint32_t d; //offset 4
uint32_t e; //offset 8
uint32_t f; //offset 12
uint32_t g; //offset 16
}; //overall alignment requirement 4, overall size 20.
struct class2
{
uint8_t a; //offset 0
uint8_t b; //offset 1
uint16_t c; //offset 2
uint32_t d; //offset 4
uint32_t e; //offset 8
// 4 bytes of padding because f has an alignment requirement of 8
uint64_t f; //offset 16
}; //overall alignment requirement 8, overall size 24
And how to prevent this?以及如何防止这种情况?
Unfortunately there is no good general solution.不幸的是,没有好的通用解决方案。
Sometimes it is possible to reduce the amount of padding by re-ordering fields, but that doesn't help in your case.有时可以通过重新排序字段来减少填充量,但这对您的情况没有帮助。 It just moves the padding around in the structure.它只是在结构中移动填充。 A structure with a field requiring 8 byte alignment will always have a size that is a multiple of 8. Therefore no matter how much you rearrange the fields your structure will always have a size of at least 24.具有需要 8 字节对齐的字段的结构的大小始终是 8 的倍数。 因此,无论您重新排列字段多少,您的结构的大小始终至少为 24。
You can use compiler-specific features such as #pragma pack
or __attribute((packed))
to force the compiler to pack the structure more tightly than normal alignment requirements would allow.您可以使用特定于编译器的功能,例如#pragma pack
或__attribute((packed))
来强制编译器比正常对齐要求所允许的更紧密地打包结构。 However, as well as limiting portability, this creates a problem when taking the address of a member or binding a reference to the member.然而,除了限制可移植性之外,这在获取成员地址或绑定对成员的引用时会产生问题。 The resulting pointer or reference may not satisfy the alignment requirements and therefore may not be safe to use.结果指针或引用可能不满足对齐要求,因此使用起来可能不安全。
Different compilers vary in how they handle this problem.不同的编译器处理这个问题的方式各不相同。 From some playing around on godbolt.来自一些在 Godbolt 上玩耍的人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.