Я сделал следующий код в качестве примера.
#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;
}
печать
20
24
8
4
Поэтому довольно просто увидеть, что один uint64_t равен двум uint32_t. Почему в классе 2 должно быть 4 дополнительных байта, если они одинаковы, за исключением замены двух uint32_t на uint64_t.
Как было указано, это связано с набивка.
Чтобы предотвратить это, вы можете использовать
#pragma pack(1)
class ... {
};
#pragma pack(pop)
Он говорит вашему компилятору выравнивать не 8 байт, а один байт. Команда pop отключает его (это очень важно, поскольку, если вы сделаете это в заголовке, а кто-то включит ваш заголовок, могут возникнуть очень странные ошибки)
Правило выравнивания (на x86 и x86_64) обычно выровнять переменную по размеру.
Другими словами, 32-разрядные переменные выровнены по 4 байта, 64-разрядные переменные по 8 байтов и т. Д.
Смещение f
12, так что в случае uint32_t f
заполнение не требуется, но когда f
является uint64_t
, 4 байта заполнения добавляются, чтобы получить f
выровнять на 8 байтов.
По этой причине лучше элементы данных заказа от самого большого до самого маленького. Тогда не было бы никакой необходимости в набивке или упаковке.