Добавлены дополнительные биты в структуре битовых полей в C
Я пытаюсь создать клиентский код C для протокола CAPWAP. Я попытался реализовать заголовок CAPWAP, используя структуру битового поля. Но после отправки этой структуры через сокет с помощью sendto(), когда я перехватываю пакет с помощью wireshark, я обнаруживаю, что между ними добавляются дополнительные биты. Я понятия не имею, откуда это взялось. Просьба о помощи. Заранее спасибо.
Я попытался прокомментировать последние несколько членов структуры, чтобы выровнять 4 байта. Тем не менее проблема сохраняется.
Это оригинальный заголовок
struct cw_header
{
unsigned preamble : 8;
unsigned hlen : 5;
unsigned rid : 5;
unsigned wbid : 5;
unsigned t : 1;
unsigned f : 1;
unsigned l : 1;
unsigned w : 1;
unsigned m : 1;
unsigned k : 1;
unsigned flags : 3;
unsigned fragment_id : 16;
unsigned fragment_offset : 13;
unsigned reserved : 3;
uint32_t mac_length : 8;
uint32_t mac_addr[6];
uint32_t padding : 8;
};
Я пытался комментировать эти
//uint32_t mac_length : 8;
//uint32_t mac_addr[6];
//uint32_t padding : 8;
Это где структура заполняется
struct cw_header create_cw_header()
{
struct cw_header cw_header;
cw_header.preamble = 0;
cw_header.hlen = 1;
cw_header.rid = 1;
cw_header.wbid = 1;
cw_header.t = 0;
cw_header.f = 0;
cw_header.l = 1;
cw_header.w = 0;
cw_header.m = 1;
cw_header.k = 0;
cw_header.flags = 0;
cw_header.fragment_id = 0; ;
cw_header.fragment_offset = 0;
cw_header.reserved = 0;
cw_header.mac_length = 6;
get_mac_address(cw_header.mac_addr);
cw_header.padding = 0;
return cw_header;
};
Вот вывод wireshark для первых 32 бит
0000 0000 0010 0001 0000 0100 0000 1010
Ожидаемый результат: биты должны быть в порядке, указанном в структуре Ошибка: дополнительные биты добавляются между членами структуры
2 ответа
"Биты должны быть в порядке, указанном в структуре"
Какой заказ? Язык C не определяет порядок следования битов. Вы не можете точно знать, если preamble
это MSB или LSB.
"Добавлены дополнительные биты между членами структуры"
Да, компилятор может свободно размещать биты заполнения или байты заполнения между членами битового поля. Битовые поля по стандарту делятся на абстрактные "невидимые" единицы, называемые "единицами хранения", которые имеют заданный размер - часто тот же размер, что и для выравнивания ЦП, но не обязательно. Между различными такими единицами хранения может быть или не быть заполнение. Компилятор также может свободно размещать биты, которые не помещаются в одну единицу хранения, в следующую.
Невозможно сказать, что будет делать конкретный компилятор, не читая о реализации битовых полей компилятором. По стандарту Си для них почти нет поддержки.
Добавьте к этому endianess, и вы получите правильный беспорядок - в вашем случае будет иметь значение как endianess CPU, так и endianess сети.
Лучшее решение всех этих проблем - полное удаление битовых полей. Вместо этого вы должны использовать побитовые операторы, которые гораздо более четко определены, детерминированы и переносимы:
uint32_t cw_preable = something << preamble_offset;
...
uint32_t cw_header = 0;
cw_header |= cw_preamble;
...
Расположение битовых полей полностью зависит от реализации. Однако, как правило, единица не может пересекать границу единицы хранения, поэтому в этом случае это будет зависеть от того, какой размер unsigned
является. Это также зависит от реализации, как uint32_t
определяется и как он сравнивается со всем остальным, и если uint32_t mac_addr[6];
может наложить остаток блока хранения в uint32_t : 8
,
В общем, это не будет красиво. Лучшим вариантом действий будет использование именованных unsigned char/uint8_t
поля и массивы в структуре, с небольшим количеством мелких элементов в них.