структура

Тема в разделе "Arduino & Shields", создана пользователем ostrov, 5 сен 2016.

  1. ostrov

    ostrov Гуру

    Сейчас вдали от компа, догадку проверить не могу, но хочется. Является ли название структуры указателем на ее первый байт, как это сделано у массивов? Задача разбить произвольную структуру с переменными на байты, то есть засунуть ее в одномерный массив соответствующего размера, а затем собрать в другом месте обратно. Передача указателя было бы самым простым и быстрым способом, как мне кажется.
     
  2. DIYMan

    DIYMan Guest

  3. ostrov

    ostrov Гуру

    Единственное что подчерпнул из ссылки: порядок следования данных в структуре не обязательно такой же как в тексте программы, что немного настораживает. Надо пробовать.
     
  4. DIYMan

    DIYMan Guest

    Так это в стандарте прописано, и только касательно членов с разной видимостью, т.е. private, protected, public. По умолчанию в struct - все члены public, и следуют друг за другом как объявлено.

    На что следует обратить внимание - так это на выравнивание элементов структуры, чаще всего именно с этим бывают непонятки. Однако, если не вру - в Arduino IDE уже выставлено выравнивание по границе байта.

    Я почему ссылку привёл - там просто чуть-чуть выдержек из стандарта, плюс описание своим языком. Ну ткнул бы вас в стандарт - там чёрт ногу сломит иногда :)
     
  5. ostrov

    ostrov Гуру

    В данном моем случае типов всего два: byte и int, и все public, то есть выравнивание вроде бы очевидно должно быть. Уже не терпится проверить это.
     
  6. ostrov

    ostrov Гуру

    В общем так. Перевести стрелки указатель со структуры на подходящий по размеру массив не удалось, пришлось выполнить перенос данных вручную в цикле. Вот пример. Объявляем две одинаковые переменные, соответствующий их размеру массив и служебную переменную-указатель:
    Код (C++):
    struct packet {
      byte Start;
      int int_1;
      int int_2;
      int int_3;
      byte Stop;
    };

    packet PAK_1;
    packet PAK_2;

    byte poB[8];
    byte *ptr;
    Заполняем PAK_1 какими то данными:
    Код (C++):
      PAK_1.Start = 10;
      PAK_1.int_1 = 500;
      PAK_1.int_2 = 700;
      PAK_1.int_3 = 900;
      PAK_1.Stop = 20;
    И переносим их из PAK_1 в PAK_2 через массив poB[]:
    Код (C++):
      ptr = &PAK_1.Start;
      for (byte i = 0; i < 8; i++) {
        poB[i] = *(ptr + i);
      }
      ptr = &PAK_2.Start;
      for (byte i = 0; i < 8; i++) {
        *(ptr + i) = poB[i];
      }
     
    В итоге PAK_2 = PAK_1. Вот эта часть мне как раз и нужна, растащить произвольную структуру по байтам, в таком виде передать (в виде пакетов данных, например со стартовыми и стоповыми байтами и КС) и собрать на другом устройстве точно в такую же структуру с такими же данными.

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

    DIYMan Guest

    Чего-то вы перемудрили ;) Структуры копировать гораздо проще:
    Код (C++):
    packet PAK_1;
    packet PAK_2;

    memcpy(&PAK_2,&PAK_1,sizeof(packet));
    Скопировали содержимое PAK_1 в PAK_2. Если уверены, что в памяти лежит структура в правильным выравниванием - то ничего не меняется: так же нужный кусок памяти копируется в структуру, и всё - незачем оперировать отдельными байтами.
     
  8. AlexU

    AlexU Гуру

    Если Вам кровь из носу нужно работать как со структурой, так и с массивом байт, то воспользуйтесь объединениями:
    Код (C++):
    union packet_u {
        struct packet packet_s;
        uint8_t packet_a[sizeof(packet)];
    };
    Код (C++):

        packet_u PAK_1;
        packet_u PAK_2;

        PAK_1.packet_s.Start = 10;
        PAK_1.packet_s.int_1 = 500;
        PAK_1.packet_s.int_2 = 700;
        PAK_1.packet_s.int_3 = 900;
        PAK_1.packet_s.Stop = 20;

        // после этого цикла структура PAK_2.packet_s будет иметь такое же содержимое
        // как и структура PAK_1.packet_s
        for (uint8_t i = 0; i < sizeof(packet); i++) {
            PAK_2.packet_a[i] = PAK_1.packet_a[i];
        }
     
    Объединения помогают рассматривать данные, как говориться, под разными углами. При этом используется один и тот же участок памяти. Хотите этот участок памяти рассматривать как структуру -- используйте выражение 'PAK_1.packet_s', хотите как массив байт -- используйте выражение 'PAK_1.packet_a'.
     
    Alex19 нравится это.
  9. ostrov

    ostrov Гуру

    Вы не поняли задачу. Структуры находятся на разных МК и мне нужно их передать разбив предварительно на байты и собрав потом снова.
     
  10. ostrov

    ostrov Гуру

    То же самое, см. выше.
     
  11. DIYMan

    DIYMan Guest

    Я прекрасно понял задачу, поэтому написал:
    Проблема одна - с выравниванием структуры: если она выровнена по границе 1 байт, то это значит, что все переменные, грубо говоря, следуют друг за другом без пробелов. Проверить очень просто:
    Код (C++):
    struct packet {
      byte Start;
      int int_1;
      int int_2;
      int int_3;
      byte Stop;
    };
     
    При выравнивании по границе 1 байт следующий код должен вывести число 8:
    Код (C++):
    Serial.println(sizeof(packet));
    Всё. Если выравнивание по границе в 1 байт (кстати, есть директивы выравнивания, типа #pragma pack) - то в структуру можно скопировать произвольный кусок памяти откуда хошь. Например, у вас пришёл набор байт, который вы вычитали в какой-либо массив байт:
    Код (C++):
    byte incoming_array[16]; // тут массив байт, пришедших откуда-либо

    packet p1, p2;

    byte* read_ptr = incoming_array;
    memcpy(&p1,read_ptr, sizeof(packet));
    read_ptr += sizeof(packet);
    memcpy(&p2,read_ptr, sizeof(packet));
     
    Вот и всё - скопировали массив байт в две структуры, без ненужного прохода в цикле.
     
    ostrov нравится это.
  12. ostrov

    ostrov Гуру

    Сдается мне, что этот memcpy тоже цикл, только в библиотеке. Но он более универсален. Попробую с ним для сравнения.
     
  13. ostrov

    ostrov Гуру

    Вот еще не понял как получить ссылку на первый байт структуры. Вот такое не проходит:
    Код (C++):
    byte *ptr = PAK;
    И такое тоже:
    Код (C++):
    byte *ptr = &PAK;
    Приходится указывает ее первый байт явно:
    Код (C++):
    byte *ptr = PAK.Start;
    А это как то некошерно.

    Причем, в качестве адреса доставки принимается нормально без конкретного члена структуры. Таким образом перекидывание данных через массив выглядит вот так:
    Код (C++):
      memcpy(poB, &PAK_1.Start, 8);

      memcpy(&PAK_2, poB, 8);
    Что странно.

    Размер указал явно, чтобы проверить нет ли пробелов. Их нет.
     
    Последнее редактирование: 6 сен 2016
  14. ostrov

    ostrov Гуру

    Да, и погонял для статистики свой цикл и memcopy 50000 раз, время первого 199 мс, второго 200 мс, то есть по скорости работы разницы нет. Вероятно алгоритм там схожий, но запись визуально более компактна, тут плюс. )
     
  15. DIYMan

    DIYMan Guest

    Всё просто:
    Код (C++):
    byte* ptr = (byte*) &PAK;
     
    ostrov нравится это.
  16. DIYMan

    DIYMan Guest

    Канэчна. Но - в среднем это более быстрый и переносимый метод, т.к. считается, что стандартные функции оптимизированы по самые не балуй.
     
  17. AlexU

    AlexU Гуру

    Вы пытались сделать следующее:
    Я показал как это делать без цикла с помощью объединений.
    Что касается цикла из моего примера, то он приводился для примера -- "сначала была структура, а теперь массив". Для Вашего случая тот цикл будет типа (или что Вы там с массивом хотели сделать):
    Код (C++):

       // первый контроллер отправляет
       for (uint8_t i = 0; i < sizeof(packet); i++) {
            someSerialInterface.send(PAK_1.packet_a[i]);
        }
     
    Код (C++):

       // второй контроллер принимает
       for (uint8_t i = 0; i < sizeof(packet); i++) {
            PAK_2.packet_a[i] = someSerialInterface.read();
        }
        // после приёма стурктура PAK_2.packet_s будет содержать те же данные,
        // что и структура на передающей стороне
     
    В этом случае писанины чуть побольше, чем с
    Код (C++):
    byte* ptr = (byte*) &PAK;
    но, IMHO, код более понятней.
     
  18. ostrov

    ostrov Гуру

    Какого размера указатель вообще? Почему в виде байта?
     
  19. DIYMan

    DIYMan Guest

    Указатель на любой тип в рамках платформы AVR имеет размерность два байта. В виде байта - вам же нужно было получить указатель на структуру как на массив байт, не так ли?
     
  20. ostrov

    ostrov Гуру

    Так, но он при этом все равно два байта? Я вот этого места не понял:
    Код (C++):
    (byte*) &PAK
    То есть в переменную-указатель отправляется ссылка приведенная к типу указатель на байт? Так?