Я не согласен со многими из приведенных здесь ответов. Я настоятельно рекомендую вам избегать соблазна наложить структуру на входящие данные. Это кажется убедительным и может даже сработать на вашей текущей цели, но если код когда-либо будет перенесен на другую цель / среду / компилятор, вы столкнетесь с проблемами. Несколько причин:
Порядок байтов: архитектура, которую вы используете сейчас, может быть прямым порядком байтов, но ваша следующая цель может быть прямым порядком байтов. Или наоборот. Вы можете преодолеть это с помощью макросов (например, ntoh и hton), но это дополнительная работа, и вы должны вызывать эти макросы каждый раз, когда ссылаетесь на поле.
Выравнивание. Используемая вами архитектура может быть способна загружать многобайтовое слово со смещением с нечетным адресом, но многие архитектуры не могут. Если 4-байтовое слово перекрывает 4-байтовую границу выравнивания, загрузка может вытащить мусор. Даже если в самом протоколе нет смещенных слов, иногда смещается сам поток байтов. (Например, хотя определение заголовка IP помещает все 4-байтовые слова на 4-байтовые границы, часто заголовок Ethernet подталкивает сам заголовок IP к 2-байтовой границе.)
Заполнение: ваш компилятор может выбрать плотную упаковку вашей структуры без заполнения или может вставить заполнение, чтобы справиться с ограничениями выравнивания цели. Я видел это изменение между двумя версиями одного и того же компилятора. Вы можете использовать #pragmas, чтобы вызвать проблему, но #pragmas, конечно, зависят от компилятора.
Порядок битов. Порядок битов внутри битовых полей C зависит от компилятора. Кроме того, для вашего кода времени выполнения трудно «добраться до битов». Каждый раз, когда вы ссылаетесь на битовое поле внутри структуры, компилятор должен использовать набор операций маски / сдвига. Конечно, вам придется делать это маскирование / смещение в какой-то момент, но лучше не делать этого при каждой ссылке, если скорость вызывает беспокойство. (Если пространство является превыше всего, используйте битовые поля, но действуйте осторожно.)
Все это не означает, что «не используйте структуры». Мой любимый подход - объявить дружественную структуру с прямым порядком байтов всех соответствующих данных протокола без каких-либо битовых полей и без заботы о проблемах, а затем написать набор симметричных подпрограмм упаковки / синтаксического анализа, которые используют эту структуру в качестве посредника.
typedef struct _MyProtocolData
{
Bool myBitA; // Using a "Bool" type wastes a lot of space, but it's fast.
Bool myBitB;
Word32 myWord; // You have a list of base types like Word32, right?
} MyProtocolData;
Void myProtocolParse(const Byte *pProtocol, MyProtocolData *pData)
{
// Somewhere, your code has to pick out the bits. Best to just do it one place.
pData->myBitA = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_A_MASK >> MY_BIT_A_SHIFT;
pData->myBitB = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_B_MASK >> MY_BIT_B_SHIFT;
// Endianness and Alignment issues go away when you fetch byte-at-a-time.
// Here, I'm assuming the protocol is big-endian.
// You could also write a library of "word fetchers" for different sizes and endiannesses.
pData->myWord = *(pProtocol + MY_WORD_OFFSET + 0) << 24;
pData->myWord += *(pProtocol + MY_WORD_OFFSET + 1) << 16;
pData->myWord += *(pProtocol + MY_WORD_OFFSET + 2) << 8;
pData->myWord += *(pProtocol + MY_WORD_OFFSET + 3);
// You could return something useful, like the end of the protocol or an error code.
}
Void myProtocolPack(const MyProtocolData *pData, Byte *pProtocol)
{
// Exercise for the reader! :)
}
Теперь остальная часть вашего кода просто манипулирует данными внутри дружественных, быстрых структурных объектов и вызывает пакет / синтаксический анализ только тогда, когда вам нужно взаимодействовать с потоком байтов. Нет необходимости в ntoh или hton, и нет битовых полей, чтобы замедлить ваш код.
person
Casey Barker
schedule
27.11.2008