Ваш компилятор корректирует размер вашей структуры, кратный его настройке выравнивания памяти. Почти все современные компиляторы делают это. На некоторых процессорах переменные и инструкции должны начинаться с на адресах памяти, кратных некоторому значению выравнивания памяти (часто 32-разрядным или 64-разрядным, но выравнивание зависит от архитектуры процессора). Большинство современных процессоров не требуют выравнивания памяти больше - но почти все они видят в существенном выигрыше в производительности. Таким образом, компиляторы выровняют ваши данные для повышения производительности.
Однако во многих случаях (например, в вашем) это не то поведение, которое вам нужно. Размер вашей структуры по разным причинам может оказаться чрезвычайно важным. В этих случаях существуют различные способы решения проблемы.
Один из вариантов - заставить компилятор использовать разные настройки выравнивания. Варианты для этого варьируются от компилятора к компилятору, поэтому вам придется проверить свою документацию. Обычно это какая-то прагма. В некоторых компиляторах (например, в компиляторах Microsoft) выравнивание памяти можно изменить только для очень небольшого фрагмента кода. Например (в VC ++):
#pragma pack(push) // save the current alignment
#pragma pack(1) // set the alignment to one byte
// Define variables that are alignment sensitive
#pragma pack(pop) // restore the alignment
Другой вариант - определить ваши переменные другими способами. Внутренние типы не меняются в зависимости от выравнивания, поэтому вместо вашего 24-битного битового поля другой подход заключается в определении вашей переменной в виде массива байтов.
Наконец, вы можете просто позволить компиляторам создавать структуры любого желаемого размера и вручную записывать размер, необходимый для чтения / записи. Пока вы не объединяете структуры вместе, это должно работать нормально. Помните, однако, что компилятор предоставляет вам мягкие структуры под капотом, поэтому, если вы создадите более крупную структуру, которая включает, скажем, works и fail struct, будет биты между ними, которые могут вызвать проблемы.
На большинстве компиляторов почти невозможно создать тип данных меньше 8 бит. Большинство архитектур просто так не думают. Это не должно быть большой проблемой, потому что большинство аппаратных устройств, которые используют типы данных менее 8 бит, в конечном итоге упорядочивают свои пакеты таким образом, что они все еще имеют 8-битные кратные, поэтому вы можете выполнять битовые манипуляции для извлечения или закодируйте значения в потоке данных, когда он уходит или входит.
По всем причинам, перечисленным выше, большая часть кода, который работает с аппаратными устройствами, такими как эта, работает с необработанными байтовыми массивами и просто кодирует данные внутри массивов. Несмотря на то, что теряются многие удобства современных языковых конструкций, в итоге все становится проще.