Выравнивание - с этим ничего не сделаешь, с этим надо жить

..
вот на пальцах смысл.
берем проц типа кортекс м0, если не ошибаюсь этот процессор не умеет обращаться к памяти без выравнивания.
делаем в нем структуру
struct str
{
int8_t A;
int8_t B;
}
он выделяет 8 байт памяти и кладет данные
A
0
0
0
B
0
0
0
работаем внутри проца и все счастливы. Теперь мы добавляем какой-то интерфейс с внешним миром, например с PC или делаем сохранение параметров в память. Радостно делаем функцию которая посылает данные, с указателем на данные и размером данных, вызываем SaveSendData(MY_STR, 2) и что? облом, структура то не 2 а 8 байт, половина не сохранилась.
вызываем SaveSendData(MY_STR, sizeof(MY_STR)), все хорошо, но в памяти отъели 8 байт если мы про это не знаем может затереть данные.
Теперь на РС делаем ответную структуру
struct str{char A; char B} и что дальше? в компе то нет выравнивания, структура 2 байта, прямое копирования и пересылка - опять облом...
казалось, ок, если такая структура плохо, делаем ее
struct __packed str
{
int8_t A;
int8_t B;
}
пересылка - сохранение в память - прием на компе - супер! вот оно счастье, но!
Проц то не умеет обращаться в не выровненые адреса, и обращение my_str.B будет всегда вести в my_str.A, хоть убейся об него, пишите В, меняете А, читаете В, получаете А. И один способ записать или изменить В, это считать 32 бита A,B,0,0, вырезать маской нужную часть, сдвинуть и считать данные, для изменения обратно, считать 32 бита, масками изменить нужный кусок, записать обратно...
То есть либо теряем скорость, либо удобство, и об этом надо думать и понимать что у вас происходит....