Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Размещение в памяти полей структур


Автор: Курсант 24.10.2014, 08:21
Всем доброго времени суток! У меня есть указатель void*, когда я пытаюсь читать из памяти структуру, расположенную по этому адресу, то первое поле читается нормально, второе - сдвинуто на 2 байта, третье - на 4 байта, то есть структура из памяти читается в искажённом виде.

Конкретно: занимаюсь разбором Ethernet-пакета, пакет считан из буфера приёмника в область памяти, есть указатель на область памяти void* Pointer. Сам пакет содержит в себе запрос от протокола ARP при подключении моего устройства в локальную сеть, хотя это не принципиально.
Код

Pointer: 
0xFF 0XFF 0xFF 0xFF 0xFF 0xFF     // MAC-адрес приёмника, широковещательный адрес.
0x01 0x23 0x45 0x67 0x89 0xAB    // MAC-адрес источника
0x08 0x06     // Двухбайтовый идентификатор протокола ARP (Address Resolution Protocol
0x00 0x00     // Какие-то данные пакета ARP
0x08 0x00     // Какие-то данные пакета ARP, по несчастью совпадающие с идентификатором протокола IP.
----------

Для удобства работы у меня есть структуры Ethernet_Header, MAC_Address, Protocol_ID_in_Ethernet
Код

struct MAC_Address {
        uint8_t MAC_HH;
        uint8_t MAC_HL;
        uint8_t MAC_MH;
        uint8_t MAC_ML;
        uint8_t MAC_LH;
        uint8_t MAC_LL;
};

struct  Protocol_ID_in_Ethernet {
        uint8_t H;
        uint8_t L;
};

struct Ethernet_Header {
        struct MAC_Address DstMACAddress;
        struct MAC_Address SrcMACAddress;
        struct Protocol_ID_in_Ethernet UpperProtocolID;
};

Работая с указателем на пакет как с указателем на структуру Ethernet_Header, я пытаюсь выяснить, пакет какого протокола содержится в данных пакета Ethernet. Для этого я считываю из области памяти, на которую указывает Pointer, структуры типа MAC_Address и Protocol_ID_in_Ethernet:
Код

struct MAC_Address tmpMAC1;
struct MAC_Address tmpMAC2;
struct Protocol_ID_in_Ethernet tmpID;

tmpMAC1 = ((struct Ethernet_Header*)Pointer)->DstMACAddress;
tmpMAC2 = ((struct Ethernet_Header*)Pointer)->SrcMACAddress;
tmpID =  ((struct Ethernet_Header*)Pointer)->UpperProtocolID;


После этого в переменных я получаю следующие значения
Код

tmpMAC1 == 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF // MAC-адрес назначения, принят без искажения
tmpMAC2 == 0x45 0x67 0x89 0xAB 0x08 0x06 // получено со сдвигом на 2 байта, поэтому последние 2 байта - идентификатор протокола ARP
tmpID == 0x08 0x00      // Сдвинуто на 4 байта, вместо идентификатора ARP получено это значение, являющееся идентификатором протокола IP. Программа работает не правильно.
.

Я исходил из того, что в структуре не предусмотрены пропуски в два байта между полями. Компилятор почему-то посчитал по другому. Я наверное попробую объявить переменную типа Ethernet_Header и считать туда весь заголовок, но вообще изначально я не хотел ничего считывать ни в какие переменные, а использовать в качестве переменной саму область памяти, используя Pointer в качестве указателя. Т.е. мне не хотелось бы заниматься копированием области памяти, скопированной из буфера приёмника - это уже явно избыточные операции.

Собственно вопрос: кто виноват и что делать? smile

З.Ы. Компилятор CMC-ARM (для контроллеров с ARM-архитектурой).

Автор: xvr 24.10.2014, 09:55
Стандарт С99, параграф 6.7.2.1 (Structure and union specifiers)
Цитата

15 There may be unnamed padding at the end of a structure or union.

так что имеет право  smile 
В С++ есть аналогичное замечание в п 9.2 абзац 21 -
Цитата

There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning,
as necessary to achieve appropriate alignment.


Попробуйте аттрибут (или прагму) packed (не знаю, как она называется у CMC-ARM)

Автор: Dem_max 24.10.2014, 10:49
используй 
Код

#pragma pack (push, 1)
typedef struct _FIRMWARE_HEADER
{
   DWORD Ident;
   DWORD FileSize;
   DWORD FirmwareSize;
   BYTE FirmwareVerMajor;
   BYTE FirmwareVerMinor;
   BYTE FirmwareUpdate;
   BYTE FirmwareVerCommand;
   WORD FirmwareVerBootloader;
} FIRMWARE_HEADER;
#pragma pack (pop)

Автор: tzirechnoy 24.10.2014, 19:23
Я посоветую не использовать прагмапаков, а работать с char *. Поначалу -- избавит от кучи проблем с пакингом, размером полей, byte order и прочей гадостью. При этом выигрыш от описания структур как структур -- в общем невелик, намакросах можно написать почти такжэ понятно, при этом скорость редко где уменьшытся, поскольку компиляторы обычно могут сокращать константные вычисления.


Автор: Курсант 24.10.2014, 21:02
Спасибо всем за ответы! Прагма пак на CMC, похоже, не работает, а для отключения выравнивания полей структур есть директива __packed. У меня всё заработало, точнее, заголовок Ethernet-пакета удалось разобрать именно на те составляющие, из которых он состоит. Но дальше возникли трудности с разбором заголовка ARP-пакета, вложенного в Ethernet-пакет. А именно, структура заголовка Ethernet-пакета описывается так:

EthernetProtocol.h
Код

__packed struct MAC_Address {
        uint8_t MAC_HH;
        uint8_t MAC_HL;
        uint8_t MAC_MH;
        uint8_t MAC_ML;
        uint8_t MAC_LH;
        uint8_t MAC_LL;
};

__packed struct Protocol_ID_in_Ethernet {
        uint8_t H;
        uint8_t L;
};

__packed struct Ethernet_Header {
        struct MAC_Address DstMACAddress;
        struct MAC_Address SrcMACAddress;
        struct Protocol_ID_in_Ethernet UpperProtocolID;
};

До этого места всё работает нормально. Но как только я поправляю аналогичным образом описание заголовков ARP-пакетов:

ARPProtocol.h
Код

__packed struct IPToMACMatch {
        struct IP_Address IPAddress;
        struct MAC_Address MACAddress;
};

__packed struct ARP_Header {
        struct Protocol_ID_in_Ethernet HTYPE;  // Hardware Type
        struct Protocol_ID_in_Ethernet PTYPE;  // Protocol Type
        uint8_t HLEN;
        uint8_t PLEN;
        uint16_t OPER;
};

__packed struct ARP_Data {
         struct MAC_Address SHA;   // Source Hardware Address
         struct IP_Address SPA;    // Source Protocol Address
         struct MAC_Address THA;   // Target Hardware Address
         struct IP_Address TPA;    // Target Protocol Address
};


Начинаются проблемы. При компиляции появляется ругань на вот этот участок кода:

EthernetProtocol.h
Код

Error[C100] EthernetProtocol.h(9,28): Structure 'MAC_Address' redefinition
__packed struct MAC_Address {
        uint8_t MAC_HH;
        uint8_t MAC_HL;
        uint8_t MAC_MH;
        uint8_t MAC_ML;
        uint8_t MAC_LH;
        uint8_t MAC_LL;
};

Error[C100] EthernetProtocol.h(18,40): Structure 'Protocol_ID_in_Ethernet' redefinition
__packed struct Protocol_ID_in_Ethernet {
        uint8_t H;
        uint8_t L;
};


при этом даже если из EthernetProtocol.h убрать директивы packed, сообщения об ошибках всё равно остаются. То есть возникают проблемы, если в packed-структуре есть другие вложенные структуры. И я опять завис.

Автор: tzirechnoy 24.10.2014, 22:44
Это у Вас не вложэнные структуры, а дважды включённые файлы заголовков.

Автор: Курсант 24.10.2014, 22:54
Цитата(tzirechnoy @ 24.10.2014,  22:44)
Это у Вас не вложэнные структуры, а дважды включённые файлы заголовков.

Ну, в файлах заголовков у меня стоит
Код

#ifndef __ETHERNETPROTOCOL_H
#define __ETHERNETPROTOCOL_H

код

#endif

Поэтому дважды включение вроде бы исключено.
А во-вторых как-то странно получается - добавляю __packed в описание структур ARP-заголовка, и сразу появляется два включения файла заголовка. Убираю __packed из описания структур ARP-заголовка - и всё компилируется (но на уровне ARP, судя по всему, работает неправильно).

Автор: tzirechnoy 25.10.2014, 11:22
Цитата
Ну, в файлах заголовков у меня стоит


То есть то, что у вас написано там вверху после слова "код" -- это фигня и не соответствует действительности.
В общем, я мог бы догадаться и сам, да.

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

Автор: Курсант 25.10.2014, 13:13
Цитата(tzirechnoy @ 25.10.2014,  11:22)
Цитата
Ну, в файлах заголовков у меня стоит


То есть то, что у вас написано там вверху после слова "код" -- это фигня и не соответствует действительности.
В общем, я мог бы догадаться и сам, да.

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

Мда, Вы правы, извините, но дело в том, что заголовочные файлы и си-файлы у меня достаточно объёмные, и приводить их полностью я посчитал излишним... Думаю, копаться в них нашлось бы ещё меньше желающих. Как ни странно, с ARP-заголовками работа идёт нормально даже без директив __packed. То ли компилятору одной директивы оказалось достаточно. Ну, я достаточно начинающий программист, поэтому пока займусь протоколами, а с директивами компилятора разберусь как-нибудь потом. Это работает, значит не будем ничего трогать. 

Спасибо большое за ответы.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)