Сейчас вдали от компа, догадку проверить не могу, но хочется. Является ли название структуры указателем на ее первый байт, как это сделано у массивов? Задача разбить произвольную структуру с переменными на байты, то есть засунуть ее в одномерный массив соответствующего размера, а затем собрать в другом месте обратно. Передача указателя было бы самым простым и быстрым способом, как мне кажется.
Единственное что подчерпнул из ссылки: порядок следования данных в структуре не обязательно такой же как в тексте программы, что немного настораживает. Надо пробовать.
Так это в стандарте прописано, и только касательно членов с разной видимостью, т.е. private, protected, public. По умолчанию в struct - все члены public, и следуют друг за другом как объявлено. На что следует обратить внимание - так это на выравнивание элементов структуры, чаще всего именно с этим бывают непонятки. Однако, если не вру - в Arduino IDE уже выставлено выравнивание по границе байта. Я почему ссылку привёл - там просто чуть-чуть выдержек из стандарта, плюс описание своим языком. Ну ткнул бы вас в стандарт - там чёрт ногу сломит иногда
В данном моем случае типов всего два: byte и int, и все public, то есть выравнивание вроде бы очевидно должно быть. Уже не терпится проверить это.
В общем так. Перевести стрелки указатель со структуры на подходящий по размеру массив не удалось, пришлось выполнить перенос данных вручную в цикле. Вот пример. Объявляем две одинаковые переменные, соответствующий их размеру массив и служебную переменную-указатель: Код (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. Вот эта часть мне как раз и нужна, растащить произвольную структуру по байтам, в таком виде передать (в виде пакетов данных, например со стартовыми и стоповыми байтами и КС) и собрать на другом устройстве точно в такую же структуру с такими же данными. Так все работает, но хотелось бы изящнее. Не знаю возможно ли перекидывание указателя в принципе? Например создать два одинаковых массива и перекинуть указатель одного на другой, чтобы оба массива указывали на один участок памяти?
Чего-то вы перемудрили Структуры копировать гораздо проще: Код (C++): packet PAK_1; packet PAK_2; memcpy(&PAK_2,&PAK_1,sizeof(packet)); Скопировали содержимое PAK_1 в PAK_2. Если уверены, что в памяти лежит структура в правильным выравниванием - то ничего не меняется: так же нужный кусок памяти копируется в структуру, и всё - незачем оперировать отдельными байтами.
Если Вам кровь из носу нужно работать как со структурой, так и с массивом байт, то воспользуйтесь объединениями: Код (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'.
Вы не поняли задачу. Структуры находятся на разных МК и мне нужно их передать разбив предварительно на байты и собрав потом снова.
Я прекрасно понял задачу, поэтому написал: Проблема одна - с выравниванием структуры: если она выровнена по границе 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)); Вот и всё - скопировали массив байт в две структуры, без ненужного прохода в цикле.
Сдается мне, что этот memcpy тоже цикл, только в библиотеке. Но он более универсален. Попробую с ним для сравнения.
Вот еще не понял как получить ссылку на первый байт структуры. Вот такое не проходит: Код (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); Что странно. Размер указал явно, чтобы проверить нет ли пробелов. Их нет.
Да, и погонял для статистики свой цикл и memcopy 50000 раз, время первого 199 мс, второго 200 мс, то есть по скорости работы разницы нет. Вероятно алгоритм там схожий, но запись визуально более компактна, тут плюс. )
Канэчна. Но - в среднем это более быстрый и переносимый метод, т.к. считается, что стандартные функции оптимизированы по самые не балуй.
Вы пытались сделать следующее: Я показал как это делать без цикла с помощью объединений. Что касается цикла из моего примера, то он приводился для примера -- "сначала была структура, а теперь массив". Для Вашего случая тот цикл будет типа (или что Вы там с массивом хотели сделать): Код (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, код более понятней.
Указатель на любой тип в рамках платформы AVR имеет размерность два байта. В виде байта - вам же нужно было получить указатель на структуру как на массив байт, не так ли?
Так, но он при этом все равно два байта? Я вот этого места не понял: Код (C++): (byte*) &PAK То есть в переменную-указатель отправляется ссылка приведенная к типу указатель на байт? Так?