Передача массивов, глобальных массивов внутри функций, указателей и объявление размеров массивов.
Я сейчас в полном замешательстве!
Я работаю над проектом, включающим Xbees в режиме API, и мне нужно отправлять в Xbee разные массивы байтов в зависимости от того, какие кнопки я нажимаю. В основном то, что у меня есть, работает нормально, но у меня возникает много проблем со следующими вопросами: Я решил сначала суммировать все свои вопросы, а затем объяснить их примерами кода.
- Arduino требует, чтобы массивы содержали определенный размер. Как я могу объявить
байтовые данные[]
без размера? - Размеры массивов должны быть объявлены с помощью числа или константного целого числа. Так как же я могу объявить
byte package[arraySize];
, когдаarraySize
передается целое числоledOn_len
, которое не является константой? - Как создать глобальный массив внутри функции?
Я взял на себя смелость сократить свой код для удобства чтения. Все, что вы видите здесь, отражает мои проблемы с моим кодом Xbee.
byte ledOn[] = { 0x7E, 0xAA, 0x1C, 0x3B }; // включаем светодиод
byte ledOff[] = { 0x7E, 0xAB, 0x1D }; // выключаем светодиод
int ledOn_len; // будет использоваться для размера массиваledOn[] НЕ константа
int ledOff_len; // будет использоваться для размера массиваledOff[] НЕ константа
// это не работает, поскольку длина массива должна быть постоянной
//int arrayLength = 4;
//байт rando[arrayLength];
// это работает нормально, поскольку arrayLength теперь является константой
//const int arrayLength = 4;
//байт rando[arrayLength];
void setup()
{
Serial.begin(115200);
// поскольку использование sizeof(data) внутри ChangeLed() не дает
// работа (он возвращает значение 2, которое соответствует размеру указателя)
// устанавливаем их здесь и передаем дальше.
ledOn_len = sizeof(ledOn);
ledOff_len = sizeof(ledOff);
}
void loop()
{
changeLed(ledOn, ledOn_len);
delay(1000);
changeLed(ledOff, ledOff_len);
delay(1000);
}
void sendToXbee()
{
//Xbee.write(пакет); // пакет не глобальный, это не работает
// да, я знаю, что не включил в эту демонстрацию серийный номер программного обеспечения
}
void changeLed(byte data[], int arraySize)
{
byte packet[arraySize]; // Как это работает???? Передача по значению
memcpy(packet, data, arraySize); // клонируем данные в пакет
// это весь код печати последовательного порта
}
1) Я создал функцию voidchangeLed()
для вычисления контрольной суммы (хрень xbee). Мне нужно передать ему массив ledOn[]
или ledOff[]
, чтобы произошли вычисления контрольной суммы. Поэтому я просто добавил voidchangeLed(byte data[])
(а затем цикл void вызывает changeLed(ledOn)
), который работает, но почему?! В другом месте, если вы объявляете массив без размера массива, там говорится, что вы должны его предоставить. Меня это сбивает с толку, но это все еще работает. . . как бы. Когда я последовательно печатаю data[i]
побайтно, он прекрасно отображает каждый байт в ledOn[]
, что я и передаю. Но когда я вызываю sizeof(data)
, он говорит, что это всего лишь 2 байта. Как он может содержать точные данные из 4 байтов, которые я ему даю, будучи всего лишь 2 байтами? Вот последовательный монитор этого. Первая строка — это распечатка data[]
, после 4 байтов указана длина, которая, как утверждается, равна 2. Вторая строка — это массив байтов packet[]
. Я установил packet[]
равным data[]
следующим образом: memcpy(packet, data, arraySize);
. Теперь вы заметите, что размер массива правильный. Интересно.
Обновление №1: Похоже, что все это каким-то образом связано с указателями. Я предполагаю, что data[]
просто указывает или ссылается на массив ledOn[]
, а использование memcpy(packet, data, arraySize);
фактически копирование массива ledOn[]
в массив packet[]
?
2) Я пытаюсь объявить случайный массив (byte rando[]
), используя int arrayLength
в качестве размера. Это не работает, поскольку размер массива должен быть постоянным. Поэтому я просто добавляю const int arrayLength
, и это работает. Большой. Итак, как мне сделать byte package[arraySize];
!? ArraySize
передается ledOn_len
, который не является константой, так как же это работает?
3) Мне нужно иметь возможность использовать массив packet[]
внутри другой функции void sendToXbee()
. Packet[]
будет моим последним массивом байтов для отправки в Xbee, содержащим либо массив ledOn[]
, либо ledOff[]
, которые отличаются длины массива. Как есть, я не могу использовать packet[]
в void sendToXbee()
, потому что packet[]
является локальным для voidchangeLed()
. Но вот загвоздка 22: я не могу объявить packet[]
в настройке, сделав его глобальным, потому что у меня еще нет размера этого массива! Размер этого массива должен быть рассчитан с помощью voidchangeLed()
, в зависимости от того, какой массив мне нужно отправить в Xbee, ledOn[]
или ledOff[]
.
Я знаю, что это много, но буду признателен за помощь! Спасибо, ребята.
@HavocRC, 👍0
1 ответ
Лучший ответ:
Arduino требует, чтобы массивы содержали определенный размер. Как я могу объявить байтовые данные[] без размера?
Вы не можете. Чистый и простой. Компилятор может определить размер по назначению времени объявления.
Размеры массивов должны быть объявлены с помощью числа или константного целого числа. Так почему же я могу объявить byte package[arraySize]; когда arraySize передается целое числоledOn_len, которое не является константой?
Это функция C99, которую GCC также включила в C++.
Как создать глобальный массив внутри функции?
Просто: вы не можете.*
Хорошо. Теперь, когда мы с этим разобрались, давайте посмотрим, что вы хотите сделать и как вам следует это сделать.
Во-первых, у вас есть несколько массивов данных:
uint8_t ledOn[] = { 0x7E, 0xAA, 0x1C, 0x3B }; // включаем светодиод
uint8_t ledOff[] = { 0x7E, 0xAB, 0x1D }; // выключаем светодиод
Эти массивы имеют связанную длину, которая равна размеру массива, разделенному на размер одной записи массива:
const int ledOn_len = sizeof(ledOn) / sizeof(ledOn[0]);
const int ledOff_len = sizeof(ledOff) / sizeof(ledOff[0]);
На данный момент вам сойдет с рук просто использование sizeof(ledOn)
, поскольку sizeof(ledOn[0])
равно 1. Но если вы когда-нибудь сделаете это с массивы чего-либо, кроме byte
, вы не застрянете, если не приобретете эту привычку.
Теперь эти массивы представляют собой просто блоки памяти фиксированного размера в фиксированном месте. Это означает, что массивы не только сами по себе являются массивами, но и являются указателями. То есть, по сути, это просто переменные, содержащие адрес, по которому хранятся данные массива.
Итак, вы передаете указатель на массив вместе с длиной массива в вашу функцию:
void changeLed(const uint8_t *data, int len) {
}
changeLed(ledOn, ledOn_len);
После этого нужно передать в функцию адрес массива, а не сам массив.
Теперь вы можете делать что-то с данными этого массива, используя этот адрес. Чего вы не можете сделать, так это изменить данные (потому что я отметил их const
).
Однако вы можете просто отправить его. Вы знаете, где находятся данные. Ты знаешь, как долго это длится. Вы знаете, куда хотите его отправить.
Итак, просто сделайте:
void changeLed(const uint8_t *data, int len) {
Xbee.write(data, len);
}
То есть запишите len
байт, начиная с адреса, на который указывает data
.
Нет необходимости иметь массивы переменного размера. Нет необходимости иметь глобальные буферы пакетов. Нет необходимости копировать какие-либо данные куда-либо с помощью memcpy()
. Просто передайте данные и отправьте их.
Если вам нужно отправить больше данных, чем просто содержимое массива, просто отправьте их. Например, вам может потребоваться (я не знаю протокол XBee) добавить в пакет верхний и нижний колонтитулы.
void changeLed(const uint8_t *data, int len) {
Xbee.write(0x01);
Xbee.write(0x02);
Xbee.write(len & 0xFF);
Xbee.write(data, len);
uint8_t cs = 0;
for (int i = 0; i < len; i++) {
cs += data[i];
}
Xbee.write(cs);
}
То, что они не вместе в одной и той же функции write
, не означает, что они не «вместе». При последовательной связи не существует понятия «вместе». Это всего лишь один байт за другим.
Мне нужно иметь возможность использовать массив package[] внутри другой функции void sendToXbee(). Packet[] будет моим последним массивом байтов для отправки в Xbee, содержащим массивledOn[] илиledOff[], которые имеют разную длину массива. В нынешнем виде я не могу использовать package[] в void sendToXbee(), потому что package[] является локальным для voidchangeLed(). Но вот загвоздка 22: я не могу объявить пакет [] в настройке, сделав его глобальным, потому что у меня еще нет размера этого массива! Размер этого массива должен быть рассчитан с помощью voidchangeLed(), в зависимости от того, какой массив мне нужно отправить в Xbee:ledOn[] илиledOff[].
Как вы думаете, почему вам нужно скопировать данные в отдельную переменную пакета? У вас уже есть данные в переменной.
Если вы действительно хотите это сделать, просто передайте packet
следующей функции точно так же, как вы уже передаете другие массивы.
* На самом деле существуют способы использования динамического распределения памяти, но я не одобряю это
- Можно ли создать массив для функций?
- Ошибка получения адреса временного массива при передаче массива составных литералов
- Невозможно преобразовать 'int (*)[size]' в 'int**': Cannot convert 'int (*)[size]' to 'int**'
- Как удалить элемент из массива arduino?
- Работает с gcc, но не с Arduino. ошибка: taking address of temporary array
- Как вернуть значение массива символов в функции Arduino IDE?
- Использование массивов, двоичных данных и битового чтения
- Установка указателя массива на null при объявлении
Очень полезно! Однако есть несколько вещей, которые приходят на ум. Во второй моей цитате говорится, что вы не можете, но под «я могу» я имел в виду, что компилятор принимает это, и это работает. Мне было любопытно, почему компилятор говорит, что так можно сделать, и почему это работает, хотя кажется, что так не должно быть., @HavocRC
Ах, я немного неправильно прочитал. Если он локальный, то да, его можно выделить с помощью переменной (начиная с C99 IIRC)., @Majenko
@HavocRC https://stackoverflow.com/questions/30694199/local-variable-length-array, @Majenko
Когда я говорю «вместе», я имею в виду сохранение всех переменных в массиве. Причина в том, что мне нужно вычислить контрольную сумму (сложение всех байтов с 0xFF, а затем вычесть это значение из 0xFF). Я написал функцию, которая вычисляет контрольную сумму на основе байтов массива. Если попытаться выполнить вычисление контрольной суммы вне массива, это был бы кошмар. Основываясь на предоставленной вами информации, я думаю, что у меня есть отличный путь к исправлению этой проблемы. Большое спасибо!, @HavocRC