这更多的是一个理论问题。
我熟悉填充和尾随填充的工作原理。
struct myStruct{
uint32_t x;
char* p;
char c;
};
// myStruct layout will compile to
// x: 4 Bytes
// padding: 4 Bytes
// *p: 8 Bytes
// c: 1 Byte
// padding: 7 Bytes
// Total: 24 Bytes
之后需要填充x
, 以便*p
已对齐,并且后面需要有尾部填充c
这样整个结构体的大小就可以被 8 整除(为了得到正确的步幅长度)。
但考虑这个例子:
struct A{
uint64_t x;
uint8_t y;
};
struct B{
struct A myStruct;
uint32_t c;
};
// Based on all information I read on internet, and based on my tinkering
// with both GCC and Clang, the layout of struct B will look like:
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// myStruct.padding: 7 Bytes
// c: 4 Bytes
// padding: 4 Bytes
// total size: 24 Bytes
// total padding: 11 Bytes
// padding overhead: 45%
// my question is, why struct A does not get "inlined" into struct B,
// and therefore why the final layout of struct B does not look like this:
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// padding 3 Bytes
// c: 4 Bytes
// total size: 16 Bytes
// total padding: 3 Bytes
// padding overhead: 19%
两种布局都满足所有变量的对齐。两种布局具有相同的变量顺序。在两种布局中struct B
具有正确的步长(可被 8 字节整除)。唯一不同的是(除了尺寸缩小了 33%)struct A
布局 2 中没有正确的步幅长度,但这应该不重要,因为显然没有数组struct A
s.
我用 -O3 和 -g 在 GCC 中检查了这个布局,struct B
有 24 字节。
我的问题是 - 是否有某种原因导致不应用此优化? C/C++ 中是否存在某些布局要求禁止这样做?或者我缺少一些编译标志吗?或者这是 ABI 的事情吗?
编辑:
回答了。
- 请参阅 @dbush 的答案,了解为什么编译器无法自行发出此布局。
- 以下代码示例使用 GCC 编译指示
packed
and aligned
(按照@jaskij的建议)手动强制执行更优化的布局。结构体B_packed
只有 16 字节而不是 24 字节(请注意,当存在结构数组时,此代码可能会导致问题/运行缓慢B_packed
,请注意,不要盲目复制此代码):
struct __attribute__ ((__packed__)) A_packed{
uint64_t x;
uint8_t y;
};
struct __attribute__ ((__packed__)) B_packed{
struct A_packed myStruct;
uint32_t c __attribute__ ((aligned(4)));
};
// Layout of B_packed will be
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// padding for c: 3 Bytes
// c: 4 Bytes
// total size: 16 Bytes
// total padding: 3 Bytes
// padding overhead: 19%