Общий размер структуры

Тема в разделе "ESP8266, ESP32", создана пользователем AlexVS, 16 июн 2020.

  1. AlexVS

    AlexVS Гик

    Объявляем 2 крайне похожие структуры:
    Код (C++):
    struct ThreeHour
    {
        time_t    Data;     //прогноз на Дату  "2014-10-10 21:00'
        int16_t   T;        //Температура
        int16_t   TT;       //Температура Min или Max
        int8_t    H;        //Влажность
        int16_t   P;        //Давление
        int8_t    WS;       //Скорость ветра
        char      WD[4]={0};//Направление
        int8_t    Cloud;    //Облачность в %
        int16_t   RainVal;  //количество осадков (*100), 0 - если нет, - снег  + дождь
        int16_t   SymNum;  
    };
    struct ThreeHourExt
    {
        time_t    Data;     //прогноз на Дату  "2014-10-10 21:00'
        int16_t   T;        //Температура
        int16_t   TT;       //Температура Min или Max
        int16_t   H;        //Влажность
        int16_t   P;        //Давление
        int16_t   WS;       //Скорость ветра
        char      WD[4]={0};//Направление
        int16_t   Cloud;    //Облачность в %
        int16_t   RainVal;  //количество осадков (*100), 0 - если нет, - снег  + дождь
        int16_t   SymNum;  
    };
    struct  ThreeHour whp3h[8];
    struct  ThreeHourExt whp3he[8];
    В коде, например в setup() пишем
    Код (C++):
    void setup(void)
    {
      char Buf[50];
      Serial.begin(115200);
      Serial.println("--------------------------------------------");
      sprintf(Buf,"Тип time_t - %u байт\r\n", sizeof(time_t));
      Serial.print(Buf);
      sprintf(Buf,"Тип int8_t - %u байт\r\n", sizeof(int8_t));
      Serial.print(Buf);
      sprintf(Buf,"Тип int16_t - %u байт\r\n", sizeof(int16_t));
      Serial.print(Buf);
      sprintf(Buf,"Тип whp3h[0] - %u байт\r\n", sizeof(whp3h[0]));
      Serial.print(Buf);
      sprintf(Buf,"Тип whp3he[0] - %u байт\r\n", sizeof(whp3he[0]));
      Serial.print(Buf);
      Serial.println("--------------------------------------------");  
    }
    Заливаем в ESP_шку и на выходе получаем следующее:
    Код (C++):
    --------------------------------------------
    Тип time_t - 4 байт
    Тип int8_t - 1 байт
    Тип int16_t - 2 байт
    Тип whp3h[0] - 24 байт
    Тип whp3he[0] - 24 байт
    --------------------------------------------
    Если же залить в ATmeg_у, то увидим ожидаемое:
    Код (C++):
    --------------------------------------------
    Тип time_t - 4 байт
    Тип int8_t - 1 байт
    Тип int16_t - 2 байт
    Тип whp3h[0] - 21 байт
    Тип whp3he[0] - 24 байт
    --------------------------------------------
     
    Не пойму, почему ESP для этих структур, дает одинаковый размер?
     
  2. SergeiL

    SergeiL Оракул Модератор

    Процессоры разные, в ESP - 32-х разрядный, компилятор выравнивает данные до четного.
    Если нужно, можно отключить выравнивание через #pragma pack
     
    AlexVS нравится это.
  3. b707

    b707 Гуру

    не до четного, а до границы слова, в ЕСП слово - 4 байта (а до четного было бы 2 байта)
     
  4. SergeiL

    SergeiL Оракул Модератор

    Тогда было бы больше, там три int8_t, каждое выравнивается до 16-ти бит
     
    AlexVS нравится это.
  5. b707

    b707 Гуру

    Да, согласен, был неправ. Значит все-таки до четного.

    Кстати. если автор чуть поменяет порядок полей, например соберет все uint8_t рядом - размер структуры уменьшится :)
     
    Igor68 нравится это.
  6. SergeiL

    SergeiL Оракул Модератор

    Вопрос, что более приоритетно.
    Скорость доступа, размер или совместимость с существующими структурами ( например структура данных передается на другое устройство)
     
  7. AlexVS

    AlexVS Гик

    В данном случае, для меня 21 или 24 байта - не принципиально. На текущей железяке, которая получала прогноз с инета, пакет занимает 21 байт. Решил перенести данный функционал на ESP и столкнулся с тем, что данные отправленные на дисплей через nRF24 искажены, начал разбираться с причиной. Про выравнивание была мысль, но подумал - какого хрена? ведь вроде бы типы данных в структуре четко обЪявлены.
    Ну что ж, изменю размер структуры на 24 байта на всех устройствах.
     
  8. SergeiL

    SergeiL Оракул Модератор

    Так а не проще ли отключить выравнивание на ESP.
    В принципе, правильнее всегда отключать выравнивание, если структура данных используется для взаимодействия с чем либо, и требуется определенная размерность данных для корректного взаимодействия.

    Код для Меги и ESP будет выглядеть так, сюрпризов от компилятора при этом не будет:

    Код (C++):
    #pragma pack(push,1)
    struct ThreeHour
    {
        time_t    Data;     //прогноз на Дату  "2014-10-10 21:00'
        int16_t   T;        //Температура
        int16_t   TT;       //Температура Min или Max
        int8_t    H;        //Влажность
        int16_t   P;        //Давление
        int8_t    WS;       //Скорость ветра
        char      WD[4]={0};//Направление
        int8_t    Cloud;    //Облачность в %
        int16_t   RainVal;  //количество осадков (*100), 0 - если нет, - снег  + дождь
        int16_t   SymNum;
    };
    struct ThreeHourExt
    {
        time_t    Data;     //прогноз на Дату  "2014-10-10 21:00'
        int16_t   T;        //Температура
        int16_t   TT;       //Температура Min или Max
        int16_t   H;        //Влажность
        int16_t   P;        //Давление
        int16_t   WS;       //Скорость ветра
        char      WD[4]={0};//Направление
        int16_t   Cloud;    //Облачность в %
        int16_t   RainVal;  //количество осадков (*100), 0 - если нет, - снег  + дождь
        int16_t   SymNum;
    };
    #pragma pack(pop)
    struct ThreeHour whp3h[8];
    struct ThreeHourExt whp3he[8];
     
    Andrey12, AlexVS, Daniil и ещё 1-му нравится это.
  9. AlexVS

    AlexVS Гик

    Спасибо. Попробую.
     
  10. RealArty

    RealArty Нерд

    AlexVS получилось у вас? Тоже самая проблема при замене меги на esp wemos данные по nrf приходят искаженные.
     
  11. Igor68

    Igor68 Гуру

    Доброго времени суток:
    Вот имею:
    Код (C++):
    typedef struct {
    int64_t TimeStamp;
    int32_t T1;
    int32_t T2;
    uint8_t flag1;
    uint8_t flag2;
    ....
    } DATA2;
    Всё нормально вроде... но некая структура (допустим эта) передаётся в TCP пакете... И всё нормально вроде.
    Это если две машины одинаковы... но если одна из них ARM, а другая x86, то размер передаваемого пакета
    разный и #pragma pack не помогает. Спасает одно - опытным путём добавляю параметры в структуру таким образом делаю их идентичными как для приёма, таки для передачи. Кстати часто выручает то, что высокую размерность переменных в структуре размещаю первыми и по её убыванию остальные так:
    Код (C++):
    uint64_t
    uint32_t
    float
    uint16_t
    int16_t
    int8_t
    но как говорил не всегда помогает что с #pragma pack, так и без оного. Это при том, что на разных машинах GCC... но если ещё для какой-то машины VisualStudio(там где Windows), то дела совсем не весёлые. Пытался всё размещать просто в массиве типа:
    Код (C++):
    #define _szB 32
    uint8_t  buff[_szB];
    ...
    #define _b_TimeStamp    0x00
    #define _b_T1                    0x04
    ... и так двлее
    и после всего так:
    Код (C++):
    uint64_t TS = (*(uint64_t*)(&buff[_b_TimeStamp]));
    ... и так далее
    не всегда помогает. Думаю потому как надо адресовать кратно размерности параметра, что часто подтверждается... хоть и не всегда(считать размерность приходится). Особенно если обмен между разными платформами... и соответственно собраны разными компиляторами.
    В моём случае надо проверять размерность/организацию структуры на разных машинах... #pragma pack работает по разному на разных компиляторах(платформах)
     
  12. b707

    b707 Гуру

    Такое может быть, так как на некоторых платформах такие типы как uint32 uint64 обязательно должны быть выравнены.
    Но если Вы сами все пакуете в байтовый массив - то никаких искажений быть не может, в этом случае ищите ошибку в коде.
     
  13. SergeiL

    SergeiL Оракул Модератор

    Я с таким не сталкивался - #pragma pack - всегда решался вопрос если переменные определены через uint8_t, а не unsigned char.
     
  14. Igor68

    Igor68 Гуру

    Именно так. uint64_t, int64_t я выравнивал кратно 8. in32_t, uint32_t, float кратно 4 соответственно и т.д... о чём и говорил ранее. Иногда приходилось вставлять "пустые" переменные... к примеру после int64_t пустая int32_t(как пример) аж 2 штуки. потому как одним компилятором будет добавлен сам, а другим нет. По смыслу это по другому... после int64_t потом int32_t и потом int16_t (то же как пример). Хотя я мог не верно указывать #pragma pack (в некоторых компиляторах он с числовым значением выравнивания, в других нет - а просто #pragma pack). Вот и такой бардак, когда с одного контроллера допустим UNO-1019(VisualStudio) по TCP посылаются пакеты на PC-Debian(GCC) ну или с MOXA IA-240LX(GCC) на PC-Debian(GCC). Как видите устройства разные и приходится делать идентичные по содержанию структуры. И пока я не выработал для себя универсальный подход для решения этого.
     
    Последнее редактирование: 15 дек 2020
  15. SergeiL

    SergeiL Оракул Модератор

  16. Igor68

    Igor68 Гуру

    Как говорил на разных машинах по разному... не всегда срабатывает. И #pragma pack в разных комптляторах мало того что работают по разному, так ещё и синтаксис разный. Довольно не удобно. Делаешь заголовочный файл, переносишь на другую машину и там ещё допиливаешь под оную.
     
  17. b707

    b707 Гуру

    делайте в виде массива - это работает всегда и везде.
     
    Andrey12 нравится это.
  18. Igor68

    Igor68 Гуру

    В 11 сообщении я упомянул этот вариант, работает. Но очень не удобно иметь набор #define бла-бла-бля.
    Благо привык к gedit, а не среды разработки... вообще-то думал, что тема закрыта...
    а я только подчеркнул необходимость проверки соответствия структур на разных платформах.
    Чисто для того что бы не "входить в ступор" при вопросах работоспособности при обмене данными на разных
    устройствах.
     
  19. SergeiL

    SergeiL Оракул Модератор

    Работать то оно должно, главное самому не ошибиться в расчетах.
    Последнюю строчку, в таком варианте, можно проще записать.
    uint64_t TS = *( (uint64_t *) (buff + _b_TimeStamp));
     
  20. Igor68

    Igor68 Гуру

    Код (C++):
    uint64_t TS = (* (uint64_t *) (&buff + _b_TimeStamp));
    //или
    uint8_t *b = &buff[0];
    uint64_t TS=(*(uint64_t*)(b + _b_TimeStamp));