Да, __attribute__((packed))
(нет необходимости во втором наборе символов подчеркивания) - это правильный способ реализации двоичных (то есть нетекстовых) сетевых протоколов. Между элементами не будет пробелов.
Однако вы должны понимать, что packed
не только упаковывает структуру, но также:
- выполняет необходимое выравнивание на один байт, а
- следит за тем, чтобы его члены, которые могут быть выровнены из-за упаковки и из-за отсутствия требования выравнивания самой структуры, были правильно прочитаны и записаны, т. Е. несоответствие его полей обрабатывается в программном обеспечении компилятором .
Однако , компилятор будет иметь дело только с смещением, если вы обращаетесь к членам структуры напрямую. Вы не должны никогда делать указатель на член упакованной структуры (кроме случаев, когда вы знаете, что для выравнивания члена требуется 1, как char или другая упакованная структура). Следующий код C демонстрирует проблему:
#include <stdio.h>
#include <inttypes.h>
#include <arpa/inet.h>
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
int main ()
{
uint8_t bytes[5] = {1, 0, 0, 0, 2};
struct packet *p = (struct packet *)bytes;
// compiler handles misalignment because it knows that
// "struct packet" is packed
printf("y=%"PRIX32", ", ntohl(p->y));
// compiler does not handle misalignment - py does not inherit
// the packed attribute
uint32_t *py = &p->y;
printf("*py=%"PRIX32"\n", ntohl(*py));
return 0;
}
В системе x86 (которая не обеспечивает выравнивание доступа к памяти) это выдаст
y=2, *py=2
как и ожидалось. С другой стороны, на моей плате ARM Linux, например, это дало, казалось бы, неправильный результат
y=2, *py=1