2016-02-06 4 views
3

Предположим, что у меня есть структура, я использую для моделирования различных форматов пакетов:SizeOf анонимным вложенная структура

#define MaxPacket 20 

typedef struct { 
    u8 packetLength; 
    union { 
     u8 bytes[MaxPacket]; 
     struct { 
      u16 field1; 
      u16 field2; 
      u16 field3; 
     } format1; 
     struct { 
      double value1; 
      double value2; 
     } format2; 
    }; 
} Packet; 

я могу ожидать, что sizeof(Packet) будет 21. Но есть ли способ сделать что-то вроде:

sizeof(Packet.format2) 

? Я пробовал это, но компилятор не доволен. Очевидно, я мог бы вытащить format1 как отдельный typedef, а затем я мог бы sizeof(format1). Но мне любопытно, нужно ли мне все это пройти. Мне нравится иерархический состав форматов. Это с gcc на 8-битном процессоре.

Мне также интересно, есть ли способ использовать вложенный тип. Если я должен сделать много

aPacketPointer->format2.value1; // not so onerous, but if the nesting gets deeper... 

Тогда иногда неплохо было бы сделать:

Packet.format2 *formatPtr = &aPacketPointer->format2; 
formatPtr->value2; // etc 

Опять же, рефакторинг в кучу предшествующего определений типов бы решить эту проблему, но тогда я теряю хороший эффект смены имен вложенных точечных ссылок.

+2

Возможно, вам будет нужно, чтобы 'sizeof (Packet)' был равен 21, но по крайней мере в двух конфигурациях компилятора, которые я пробовал на нем, это получается до 32. Возможно, это не будет проблемой для вашего использования, поскольку, по крайней мере, он больше, чем вы ожидаете (с другой стороны, может быть, это проблема). Что-то еще, что может вас удивить, состоит в том, что смещение 'union', содержащего' bytes', 'format' и' format2', составляло 8 - компиляторы помещали прокладку между полем 'packetLength' и объединением. Вы должны быть очень осторожны при моделировании проводных объектов данных в структуре C/C++. –

+0

Имейте в виду, что @MichaelBurr, вероятно, тестировал 32-битную архитектуру, где по умолчанию все заполняется. 8b компиляторы процессора не заполняют. Ничего, что я видел в любом случае. – Larry

+0

Да. Кастинг байтов как структур для более проводных протоколов начинается с основного предположения, что вы можете принудительно и доверять макетам структуры. У разных компиляторов обычно есть разные механизмы для этого. –

ответ

3

За то, что будет работать даже в C90, вы можете использовать макрос, смоделированный на средства компиляции-х offsetof() макрос:

#define sizeof_field(s,m) (sizeof((((s*)0)->m))) 

Настраивается соответственно, если ваш набора инструментов-х offsetof() макрос не основан на литье 0 на указатель на тип структуры.

Когда я использую его следующим образом:

std::cout << sizeof_field(Packet,format1) << std::endl; 
std::cout << sizeof_field(Packet,format2) << std::endl; 

Я получаю результат:

6 
16 

Для вашего второго вопроса, если вы готовы полагаться на typeof расширение GCC в вы можете создать аналогичный макрос для объявления указателей на ваши вложенные анонимные структуры:

#define typeof_field(s,m) typeof(((s*)0)->m) 

... 
typeof_field(Packet,format2)* f2 = &foo.format2; 

Если быть честным , Я нахожу, что конструкция довольно уродливая, но она может быть лучше других доступных вам опций.

Документы GCC о том, что «операнд типаof оценивается по его побочным эффектам тогда и только тогда, когда он является выражением измененного типа или имени такого типа», поэтому кажущееся обращение нулевого указателя не должно приводить к неопределенному когда массив переменной длины не задействован.

+2

примечание: в этом случае разрешено разыменовывать нулевой указатель внутри sizeof, так как операнд sizeof не оценивается (поскольку это не VLA). (То же самое нельзя сказать о пользовательских смещениях клонов) –

1

Вы можете просто присвоить этим вложенным структурам имя, не нужно вводить typedef. Например:

typedef struct { 
    u8 packetLength; 
    union { 
     u8 bytes[MaxPacket]; 
     struct myformat1 { 
      u16 field1; 
      u16 field2; 
      u16 field3; 
     } format1; 
     struct myformat2 { 
      double value1; 
      double value2; 
     } format2; 
    }; 
} Packet; 

Тогда вы можете написать, например. sizeof(struct myformat1), объявить переменные этого типа и т. Д.

Вы также можете добавить typedef впоследствии, например.

typedef struct myformat1 myformat1; 
2

Используя C11 или C99, создайте фиктивный составной литерал и найдите его размер.

printf("%zu\n", sizeof( ((Packet){ 0, { "" }}).format2 )); 

Выход

16 
Смежные вопросы