Передача двумерного массива в функцию

Я работаю над проектом светодиодной матрицы, где запускаю серию паттернов, сделанных из растровых кадров.

Вот пример: https://vimeo.com/564184465

Прямо сейчас я использую серию вызовов методов с именем per pattern для запуска анимации:

void CSMatrix::runPattern(PATTERNS pattern, uint8_t totalRuns)
{
  for (uint8_t i = 0; i < totalRuns; i++)
  {
    switch (pattern)
    {
    case PATTERN_ZIG_ZAG:
      runPatternSpacedStripes();
      break;
    case PATTERN_ARROW_UP:
      runPatternArrowUp();
      break;
...
    }
  }
}

void CSMatrix::runPatternSpacedStripes()
{
  for (uint8_t i = 0; i < SPACED_STRIPES_LEN; i++)
  {
    renderFrame(SPACED_STRIPES[i]);
    FastLED.show();
    delay(100);
  }
}

void CSMatrix::runPatternArrowUp()
{
  for (uint8_t i = 0; i < ARROW_UP_LEN; i++)
  {
    renderFrame(ARROW_UP[i]);
    FastLED.show();
    delay(100);
  }
}

...

А растровые изображения, используемые для управления состояниями пикселей матрицы, выглядят следующим образом (функции под renderFrame в основном говорят: "если это 1, используйте этот цвет, если это 0, используйте этот цвет":

const byte SPACED_STRIPES[][8] = {
    {0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000},
    {0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100},
...    
};
const int SPACED_STRIPES_LEN = sizeof(SPACED_STRIPES) / 8;

Это работает нормально, но иметь кучу одинаковых методов для запуска каждого шаблона не очень хорошо.

Итак, после этой работы я решил провести рефакторинг, чтобы создать общий метод runTwoColorPattern, который мог бы принимать кадры шаблона и длину для запуска данного шаблона:

void CSMatrix::runTwoColorPattern(const byte *frames, const int length, int delayDuration)
{
  for (uint8_t i = 0; i < length; i++)
  {
    renderFrame(frames[i]);
    FastLED.show();
    delay(delayDuration);
  }
}

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

Я попытался обновить свой оператор switch, передав указатель SPACED_STRIPES в runTwoColorPattern:

    switch (pattern)
    {
    case PATTERN_ZIG_ZAG:
      runTwoColorPattern(SPACED_STRIPES, SPACED_STRIPES_LEN, 100);
      //runPatternSpacedStripes();
      break;

Но когда я это делаю, я получаю ошибку

аргумент типа "const byte (*)[8]" несовместим с параметром типа "const byte *"C/C++(167)

error

Я обнаружил, что могу исправить это, разыменовав первый элемент многомерного массива (что означало бы, что я передаю адрес памяти первому элементу указателя многомерного массива (я думаю)), но когда я делаю это, шаблон работает в обратном направлении и дрожит :|

https://vimeo.com/564184624

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

Есть какие-нибудь предложения?

, 👍0

Обсуждение

Для этого вам действительно не нужен 2D - массив-подойдет простой 1D-массив и просто используйте смещения, кратные 8, для чтения каждого кадра., @Majenko

Ааааааа это правда! Я работал с этим форматом b/c, было легко визуально проанализировать и настроить растровые изображения вручную после их генерации из онлайн-калькулятора, но в конечном счете этот формат не нужен. Итак, чтобы повторить, чтобы убедиться, что я понимаю: я могу сделать массив 1d, где каждый элемент представляет собой одно числовое представление кадра, и эти числа могут быть проанализированы по 8 бит за раз, чтобы получить строки внутри кадра, верно?, @Chris Schmitz

Нет, у вас просто будет большой "blob" из 8-битных значений. Точно так же, как у вас сейчас, но без}, {между каждым кадром. Затем кадр 0 начинается со смещения 0. Кадр 1 со смещением 8. Рамка 2 со смещением 16 и т. Д. В основном &frames[i * 8], @Majenko

а, еще проще. Я попробую, и когда он заработает, я опубликую ответ. Спасибо за помощь!!, @Chris Schmitz


2 ответа


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

1

2D - массив будет передаваться в виде кадров const byte[][8]. Однако вам действительно не нужна сложность 2D - массива-вы можете сделать это с помощью 1D-массива.

Ваш массив будет выглядеть следующим образом:

const byte SPACED_STRIPES[] = {
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,

     0b01100011,
     0b11000110,
     0b10001100,
     0b00011000,
     0b00110001,
     0b01100011,
     0b11000110,
     0b10001100,
...    
};

И вы ссылаетесь на фреймы внутри него как на кратное 8 индексу. Используя ваш существующий код, это будет выглядеть так:

void CSMatrix::runTwoColorPattern(const byte *frames, const int length, int delayDuration)
{
  for (uint8_t i = 0; i < length; i++)
  {
    renderFrame(&frames[i * 8]); // Взять адрес первого значения кадра, кратного 8
    FastLED.show();
    delay(delayDuration);
  }
}
,

Потрясающе, это сработало! Я обновил свой шаблон и метод, и теперь анимация работает так, как ожидалось. Реализация изменений также помогла мне понять, к чему именно вы стремитесь; структура многомерного массива помогла мне визуально, но не является необходимой для синтаксического анализа. Другое дополнительное преимущество такого способа заключается в том, что теперь, когда это плоский массив, я могу отправлять большие двоичные объекты произвольного размера для матриц разного размера (16x16, 32x32 ...) и просто параметризовать множитель для захвата индекса указателя. Еще раз спасибо за помощь!, @Chris Schmitz

Re “const byte *frames[8]”: вы имеете в виду const byte (*frames)[8]., @Edgar Bonet

@EdgarBonet Я никогда в жизни не видел такого формата. На самом деле я имел в виду " кадры[][8]. Является ли (*frames)[8]` каким-то более новым синтаксисом C++, которого не существовало, когда меня учили C в университете?, @Majenko

AFAIK, `(*frames)[8] ' - это ANSI C, может быть, даже K&R C., @Edgar Bonet

Ну, я когда-либо сталкивался с такими скобками только при работе с указателями функций., @Majenko

@EdgarBonet Я рыскал по сети, и я нашел ровно один пример, использующий этот формат, без объяснения того, что это такое и как это работает. Я нашел тот, который использует (*name)[X][Y], который утверждает, что передает указатель на 2D-массив, и который я могу полностью понять. Я думаю, что (*name)[X] передает 2D-массив в качестве указателя на 1D-массив. Действительный, но какой-то взлом. Кажется, нигде он не используется, все примеры либо " *name []", либо "**name", либо `name[][X]"., @Majenko

Вы найдете пару примеров с объяснением, если будете искать “указатель C на массив” в stackoverflow. Это не хак, это стандартный синтаксис, хотя и достаточно запутанный для большинства людей, чтобы избежать его. [name[][X] неявно переводится компилятором в (*name)[X]](http://c-faq.com/aryptr/aryptrparam.html), но этот перевод выполняется только в объявлениях параметров функций. См. также [Как объявить указатель на массив?](http://c-faq.com/aryptr/ptrtoarray.html) и [Как передать двумерный массив](http://c-faq.com/aryptr/pass2dary.html) в разделе C FAQ., @Edgar Bonet


2

Сообщение об ошибке довольно явное:

аргумент типа "const byte (*)[8]" несовместим с параметром типа "const byte *".

Если вы хотите передать SPACED_STRIPES методу, он будет передан как const byte (*)[8], то есть указатель на массивы из 8 байтов. Вы можете просто установить тип параметра соответствующим образом:

void CSMatrix::runTwoColorPattern(
    const byte (*frames)[8], const int length, int delayDuration)
{
    // тело без изменений
}
,

Спасибо за этот ответ. В итоге я предложил упростить массив, но этот ответ помогает мне понять ошибку, которую я получал. Я не понимал, что могу написать сигнатуру метода с помощью паренов вокруг указателя., @Chris Schmitz