Передача массивов, глобальных массивов внутри функций, указателей и объявление размеров массивов.

Я сейчас в полном замешательстве!

Я работаю над проектом, включающим Xbees в режиме API, и мне нужно отправлять в Xbee разные массивы байтов в зависимости от того, какие кнопки я нажимаю. В основном то, что у меня есть, работает нормально, но у меня возникает много проблем со следующими вопросами: Я решил сначала суммировать все свои вопросы, а затем объяснить их примерами кода.

  1. Arduino требует, чтобы массивы содержали определенный размер. Как я могу объявить байтовые данные[] без размера?
  2. Размеры массивов должны быть объявлены с помощью числа или константного целого числа. Так как же я могу объявить byte package[arraySize];, когда arraySize передается целое число ledOn_len, которое не является константой?
  3. Как создать глобальный массив внутри функции?

Я взял на себя смелость сократить свой код для удобства чтения. Все, что вы видите здесь, отражает мои проблемы с моим кодом 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[] .

Я знаю, что это много, но буду признателен за помощь! Спасибо, ребята.

, 👍0


1 ответ


Лучший ответ:

0

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 следующей функции точно так же, как вы уже передаете другие массивы.


* На самом деле существуют способы использования динамического распределения памяти, но я не одобряю это

,

Очень полезно! Однако есть несколько вещей, которые приходят на ум. Во второй моей цитате говорится, что вы не можете, но под «я могу» я имел в виду, что компилятор принимает это, и это работает. Мне было любопытно, почему компилятор говорит, что так можно сделать, и почему это работает, хотя кажется, что так не должно быть., @HavocRC

Ах, я немного неправильно прочитал. Если он локальный, то да, его можно выделить с помощью переменной (начиная с C99 IIRC)., @Majenko

@HavocRC https://stackoverflow.com/questions/30694199/local-variable-length-array, @Majenko

Когда я говорю «вместе», я имею в виду сохранение всех переменных в массиве. Причина в том, что мне нужно вычислить контрольную сумму (сложение всех байтов с 0xFF, а затем вычесть это значение из 0xFF). Я написал функцию, которая вычисляет контрольную сумму на основе байтов массива. Если попытаться выполнить вычисление контрольной суммы вне массива, это был бы кошмар. Основываясь на предоставленной вами информации, я думаю, что у меня есть отличный путь к исправлению этой проблемы. Большое спасибо!, @HavocRC