Question

I've written CMyObject class as follows:

class CMyObject 
{
public:

CMyOjbect(void) {};
virtual ~CMyOjbect(void) {};

public:
    ULONGLONG m_uField1;
    UINT m_uField2;
    BOOL m_bField3;
    int m_iField4;
    BOOL m_bField5;
}

To reduce the size of CMyObject, I changed it to:

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;
}

However, the sizeof(CMyObject) is still not changed, why?

Can I use pargma pack(1) in a class to pack all the member variables, like this:

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)

No correct solution

OTHER TIPS

Because of your ULONGLONG first member, your structure will have 8-byte (64-bit) alignment. Assuming 32-bit ints, your first version uses 18 bytes, which would take 24 bytes to store. (The large and small members are interspersed, which makes the matter worse, but by my count, that doesn't change the answer here.) Your second version also takes 18 bytes:

  • 8 bytes for m_uField1
  • 4 bytes for m_uField2
  • 2 bytes for m_sField4
  • 4 bytes for the two unsigned bitfields (which will have 4-byte alignment, so will also inject 2 bytes of padding after m_sField4)

If you switch to short unsigned m_bField3:1 and short unsigned m_bField4:1, I think there's a good chance your structure will become smaller and fit in only 16 bytes.

Use of #pragma pack is not portable, so I can't comment on the specifics there, but it's possible that could shrink the size of your structure. I'm guessing it may not, though, since it's usually better at compensating for nonoptimal ordering of members with alignment, and by my counts, your variables themselves are just too big. (However, removing the alignment requirement on the ULONGLONG may shrink your structure size in either version, and #pragma pack may do that.)

As @slater mentions, in order to decrease the size and padding in your structure, you should declare your member variables of similar sizes next to each other. It's a pretty good rule of thumb to declare your variables in decreasing size order, which will tend to minimize padding and leverage coinciding alignment requirements.

However, the size of the structure isn't always the most important concern. Members are initialized in declaration order in the constructor, and for some classes, this matters, so you should take that into account. Additionally, if your structure spans multiple cache lines and will be used concurrently by multiple threads, you should ideally put variables that are used together nearby and in the same cache line and variables that are not used together in separate cache lines to reduce/eliminate false sharing.

In regards to your first question "However, the sizeof(CMyObject) is still not changed, why?"

Your BOOLs are not defined contiguously, so they are padded by the compiler for the purposes of memory-alignment.

On most 32-bit systems, this struct uses 16 bytes:

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
}

This struct uses 12 bytes:

struct {
  char b1; // 1 byte
  char b2; // 1 byte, 2  bytes for padding
  int i1;  // 4 bytes
  int i2;  // 4 bytes
}

Alignment and packing are implementation dependent, but typically you can request smaller alignment and better packing by using smaller types. That applies to specifying smaller types in bit-field declarations as well, since many compilers interpret the bit-field type as a request for allocation unit of that size specifically and alignment requirement of that type specifically.

In your case one obvious mistake is using unsigned for bit fields. Use unsigned char for bit fields and it should pack much better

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;
};

This will not necessarily make it as compact as #pragma pack(1) can make it, but it will take it much closer to it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top