Поэтому я работаю над созданием эхо-запроса ICMPv4 и решил запустить мою собственную структуру для хранения пакета. Чтобы сделать идентификацию пакета легко идентифицировать в wirehark, я решил поместить abcde в поле данных.
struct icmpPacket{
u_int8_t icmp_type:8, icmp_code:8;
u_int16_t icmp_checksum:16, icmp_id:16, icmp_seqnum:16;
char icmp_data[6]; //cheat a little bit, set the field just large enough to store "abcde";
} __attribute__((aligned (16))) icmppckt; // icmp has an 8 byte header + 6 bytes of data
Я застрял в том, как заставить компилятор читать структуру как серию 16-битных слов
Стандартный способ для этого - через memcpy
:
icmpPacket packet = { /* ... */ };
uint16_t buf[sizeof(icmpPacket) / sizeof(uint16_t)];
memcpy(buf, &packet, sizeof(icmpPacket));
/* Now use buf */
Современные компиляторы достаточно умны, чтобы оптимизировать их соответствующим образом, фактически не выполняя вызов функции. См. Примеры с clang и g++).
Общее расширение компилятора позволяет использовать союзы, хотя это неопределенное поведение в стандарте C++:
union packet_view{
icmpPacket packet;
uint16_t buf[sizeof(icmpPacket) / sizeof(uint16_t)];
};
icmpPacket packet = { /* ... */ };
packet_view view;
view.packet = packet;
/* Now read from view.buf. This is technically UB in C++ but most compilers define it. */
Использование reinterpret_cast<uint16_t*>(&packet)
или его эквивалент C нарушит строгие правила псевдонимов и приведет к неопределенному поведению. §3.10 [basic.lval]/p10 стандарта C++:
Если программа пытается получить доступ к сохраненному значению объекта с помощью glvalue, отличного от одного из следующих типов, поведение не определено:
- динамический тип объекта,
- cv-квалифицированная версия динамического типа объекта,
- тип, аналогичный (как определено в 4.4) для динамического типа объекта,
- тип, который является подписанным или неподписанным типом, соответствующим динамическому типу объекта,
- тип, который является подписанным или неподписанным типом, соответствующим версии с динамическим типом объекта cv,
- совокупный или тип объединения, который включает один из вышеупомянутых типов среди его элементов или нестатических членов данных (включая рекурсивно элемент или нестатический элемент данных субагрегата или содержащегося объединения),
- тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,
- char или unsigned char type.
Аналогично, в §6.5/p7 из C11 говорится:
Объект должен иметь сохраненное значение, к которому обращается только выражение lvalue, которое имеет один из следующих типов:
- тип, совместимый с эффективным типом объекта,
- квалифицированную версию типа, совместимую с эффективным типом объекта,
- тип, который является подписанным или неподписанным типом, соответствующим эффективному типу объекта,
- тип, который является подписанным или неподписанным типом, соответствующим квалифицированной версии эффективного типа объекта,
- совокупный или союзный тип, который включает один из вышеупомянутых типов среди его членов (включая рекурсивно, член субагрегата или объединенного объединения) или
- тип символа.
вы можете использовать 16-битные указатели для этого
в C++ вы можете сделать это следующим образом:
#pragma pack(1)
struct icmpPacket
{
u_int8_t icmp_type:8, icmp_code:8;
u_int16_t icmp_checksum:16, icmp_id:16, icmp_seqnum:16;
char icmp_data[6]; //cheat a little bit, set the field just large enough to store "abcde";
} icmppckt; // icmp has an 8 byte header + 6 bytes of data
WORD *picmppckt16=(WORD*)((void*)&icmppckt);
#pragma pack()
измените WORD на 16-битный тип данных, который ваш компилятор знает...
union
но я не знаю, имеете ли вы это в виду.