Почему размер моего объекта не уменьшается?

Я написала 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)

1

Решение

Из-за вашего ULONGLONG первый член, ваша структура будет иметь 8-байтовое (64-битное) выравнивание. Предполагая 32-битные числа, ваша первая версия использует 18 байтов, что займет 24 байта для хранения. (Большие и маленькие элементы перемежаются, что усугубляет ситуацию, но, по моим подсчетам, это не меняет ответа.) Ваша вторая версия также занимает 18 байт:

  • 8 байт для m_uField1
  • 4 байта для m_uField2
  • 2 байта для m_sField4
  • 4 байта для двоих unsigned битовые поля (которые будут иметь 4-байтовое выравнивание, поэтому также добавят 2 байта после заполнения m_sField4)

Если вы переключитесь на short unsigned m_bField3:1 а также short unsigned m_bField4:1Я думаю, что есть большая вероятность, что ваша структура станет меньше и поместится всего в 16 байтов.

Использование #pragma pack Я не могу комментировать специфику, но возможно, что это может уменьшить размер вашей структуры. Я предполагаю, что это может и не произойти, так как обычно лучше компенсировать неоптимальное упорядочение элементов с выравниванием, и, по моим подсчетам, ваши переменные слишком велики. (Однако, снятие требования выравнивания на ULONGLONG может уменьшить размер вашей структуры в любой версии, и #pragma pack может сделать это.)

Как упоминает @slater, чтобы уменьшить размер и отступы в вашей структуре, вы должны объявлять переменные-члены одинакового размера рядом друг с другом. Это очень хорошее правило — объявлять переменные в порядке уменьшения размера, что, как правило, сводит к минимуму заполнение и использование совпадающих требований выравнивания.

Тем не менее, размер структуры не всегда самая важная проблема. Члены инициализируются в порядке объявления в конструкторе, и для некоторых классов это имеет значение, поэтому вы должны принять это во внимание. Кроме того, если ваша структура охватывает несколько строк кэша и будет использоваться одновременно несколькими потоками, в идеале следует поместить переменные, которые используются вместе, рядом и в одной строке кэша, и переменные, которые не используются вместе, в отдельные строки кэша, чтобы уменьшить / исключить ложное. обмен.

4

Другие решения

Что касается вашего первого вопроса «Тем не менее, sizeof(CMyObject) до сих пор не изменилось, почему?

Ваш BOOLs не определены непрерывно, поэтому они дополняются компилятором для выравнивания памяти.

В большинстве 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

Выравнивание и упаковка зависят от реализации, но обычно вы можете запросить меньшее выравнивание и лучшую упаковку, используя меньшие типы. Это также относится и к указанию меньших типов в объявлениях битовых полей, поскольку многие компиляторы интерпретируют тип битового поля как запрос на единицу выделения этого размера конкретно и требование выравнивания этого типа конкретно.

В вашем случае одна очевидная ошибка заключается в использовании 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) может сделать это, но это будет гораздо ближе к нему.

1
По вопросам рекламы [email protected]