我写了CMyObject
课程如下:
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
BOOL m_bField3;
int m_iField4;
BOOL m_bField5;
}
要缩小CMyObject
的尺寸,我将其更改为:
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4; // Change from int to short, since short is enough for the data range
unsigned m_bField3: 1; // Change field 3 and 5 from BOOL to bit field to save spaces
unsigned m_bField5: 1;
}
然而,sizeof(CMyObject)
仍未改变,为什么?
我可以在pargma pack(1)
中使用class
打包所有成员变量,如下所示:
pargma pack(1)
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4; // Change from int to short, since short is enough for the data range
unsigned m_bField3: 1; // Change field 3 and 5 from BOOL to bit field to save spaces
unsigned m_bField5: 1;
}
pargma pack(0)
答案 0 :(得分:4)
由于您的ULONGLONG
第一个成员,您的结构将具有8字节(64位)对齐。假设32位整数,您的第一个版本使用18个字节,这将需要24个字节来存储。 (大小成员穿插,这使问题变得更糟,但据我的统计,这不会改变答案。)你的第二个版本也需要18个字节:
m_uField1
m_uField2
m_sField4
unsigned
位域(将具有4字节对齐,因此在m_sField4
之后也会注入2个字节的填充)如果切换到short unsigned m_bField3:1
和short unsigned m_bField4:1
,我认为您的结构很有可能变得更小并且仅适合16个字节。
#pragma pack
的使用不可移植,因此我无法对其中的细节进行评论,但可能会缩小结构的大小。我猜它可能不会,因为它通常更好地补偿了对齐的成员的非最佳排序,而且根据我的统计,你的变量本身就太大了。 (但是,删除ULONGLONG
上的对齐要求可能会缩小任一版本中的结构大小,而#pragma pack
可能会这样做。)
正如@slater所提到的,为了减小结构中的大小和填充,你应该声明相似大小的成员变量彼此相邻。以递减的大小顺序声明变量是一个非常好的经验法则,这将倾向于最小化填充并利用重合的对齐要求。
然而,结构的大小并不总是最重要的问题。成员在构造函数中按声明顺序初始化,对于某些类,这很重要,因此您应该考虑到这一点。此外,如果您的结构跨越多个缓存行并且将由多个线程同时使用,则理想情况下应将在一起使用的变量放在同一缓存行中,并将未在单独缓存行中一起使用的变量放在一起以减少/消除false共享。
答案 1 :(得分:2)
关于您的第一个问题“然而,sizeof(CMyObject)
仍未改变,为什么?”
您的BOOL
没有连续定义,因此编译器将它们填充以进行内存对齐。
在大多数32位系统上,此结构使用16个字节:
struct {
char b1; // 1 byte for char, 3 for padding
int i1; // 4 bytes
char b2; // 1 byte for char, 3 for padding
int i2; // 4 bytes
}
此结构使用12个字节:
struct {
char b1; // 1 byte
char b2; // 1 byte, 2 bytes for padding
int i1; // 4 bytes
int i2; // 4 bytes
}
答案 2 :(得分:1)
对齐和打包是依赖于实现的,但通常可以通过使用较小的类型来请求更小的对齐和更好的打包。这也适用于在位字段声明中指定较小的类型,因为许多编译器将位字段类型解释为特定于该大小的分配单元的请求以及该类型的对齐要求。
在您的情况下,一个明显的错误是使用unsigned
用于位字段。使用unsigned char
作为位字段,它应该包装好得多
class CMyObject
{
public:
CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};
public:
ULONGLONG m_uField1;
UINT m_uField2;
short m_sField4;
unsigned char m_bField3: 1;
unsigned char m_bField5: 1;
};
这不一定会像#pragma pack(1)
那样紧凑,但它会更接近它。