1

I think I lost my whole C++ knowledge...

What I want is to initialize a 2D unsigned char array in a proper (readable) way: My approaches:

#define RADIO_ONOFF 0
#define RADIO_P1 1
#define RADIO_P2 2
... 

#define NR_OF_CODES = 5
#define CODE_LENGTH = 10

#1:

unsigned char** codes = new unsigned char*[NR_OF_CODES];
codes[RADIO_ONOFF] = new unsigned char[CODE_LENGTH]{ 9,180,88,11,33,4,0,255,64,191 }; //     does not work
...

#2:

unsigned char ICODES[NR_OF_CODES][CODE_LENGTH];
ICODES[RADIO_ONOFF] = { 9,180,88,11,33,4,0,255,64,191 };  // same as above
...

#3:

class Test {
  private:
    unsigned char data[CODE_LENGTH];

  public:
    Test(unsigned char a1, unsigned char a2, unsigned char a3, unsigned char a4, unsigned char a5, unsigned char a6, unsigned char a7, unsigned char a8, unsigned char a9, unsigned char a10);

    unsigned char* getData(void);
};

Test::Test(unsigned char a1, unsigned char a2, unsigned char a3, unsigned char a4, unsigned char a5, unsigned char a6, unsigned char a7, unsigned char a8, unsigned char a9, unsigned char a10) {
  this->data[0] = a1;
  this->data[1] = a2;
  this->data[2] = a3;
  this->data[3] = a4;
  this->data[4] = a5;
  this->data[5] = a6;
  this->data[6] = a7;
  this->data[7] = a8;
  this->data[8] = a9;
  this->data[9] = a10;
}

unsigned char* Test::getData(void) {
  return data;
}

void setup() {

  test[RADIO_ONOFF] = new Test( 9,180,88,11,33,4,0,255,64,191 );
  test[RADIO_P1] = new Test( 9,180,88,11,33,4,0,255,64,192 );
  ...
}

#4

const unsigned char RADIO_ONOFF[]  = { 9,180,88,11,33,4,0,255,64,191 };
const unsigned char RADIO_P1[]     = { 9,180,88,11,33,4,0,255,64,192 };
...

Error message I get for #1 and #2: (code should compile for Arduino and it needs a setup-function)

In function 'void setup()': revTest:58: error: expected primary-expression before '{' token revTest:58: error: expected `;' before '{' token

OK - my questions:

To me - #3 and #4 are nice and readable. Effort for #3 is the highest - but I think it´s the fastest way if I want do uses the array in a switch statement. - True?

I thought array initialization in #1 and #2 should work this way????

Very Arduino specific:

I am not sure what has to be defined inside of setup() and what should go outside of setup(). static initialization and global outside, dynamic inside or what?

I have red about PROGMEM for Arduino - I think it´s not worth the effort in this case. I am right? (I think I`ll have about 50 different codes...)

thx!

Mike Mitterer
  • 6,810
  • 4
  • 41
  • 62

2 Answers2

2

Before answering your questions, let's look at what's going wrong with solutions 1 and 2.

The problem with solutions 1 and 2 is that you're using an initialiser ( i.e. the `{} syntax ), where your compiler is expecting an assignment expression.

Initialisation syntax can only be used at the point where the variable is declared. Roughly speaking, initialisation should be understood as giving a value to a variable as soon as it is constructed. Assignment should be understood as giving a value to a variable that has already been constructed somewhere else.

So when you do:

unsigned char** codes = new unsigned char*[NR_OF_CODES];

You are doing initialisation. You are initialising the variable codes to a freshly allocated array of pointers to unsigned char.

On the next line, you are then telling the compiler you are doing an assignment. You're using the = sign and assigning to codes, which was declared on the previous line - compiler sees this as assignment.

codes[RADIO_ONOFF] = new unsigned char[CODE_LENGTH] ... 

But then, immediately afterwards, you attempt to use initialisation syntax.

Your compiler is complaining because it's read this:

codes[RADIO_ONOFF] = new unsigned char[CODE_LENGTH] ... 

as a complete expression. It read that as you're allocating an array of CODE_LENGTH bytes and assigning it to the RADIO_ONOFF th member of codes.

And it expected you to stop there, with a semi colon but you continued and added the initalisation syntax {}. It doesn't understand, because you are mixing assignment and initialisation together - which are two separate things. That's why you're getting the "I expect a semi colon" type error from the compiler.

To answer your questions, both solution 3 and 4 are taking the long way round. There are quicker ways of initialising a 2D array in C++. I also agree with previous answer about using uint8_t.

Would something like this be more suitable?

uint8_t codes[CODE_LENGTH][NR_OF_CODES] = 
                              {{0, 1, 2, 3, 4, 5, 6, 7}, 
                              {0, 1, 2, 3, 4, 5, 6, 7}, 
                              {0, 1, 2, 3, 4, 5, 6, 7}, 
                              {0, 1, 2, 3, 4, 5, 6, 7} };

A good way to read array syntax is to take the final (rightmost) value in the square brackets [] as the size of some array, and then work backwards.

So uint8_t codes[CODE_LENGTH][NR_OF_CODES] will decode out to an array of size NR_OF_CODES of something. And to get that something go left, and we see uint8_t codes[CODE_LENGTH] - so it's an array of length NR_OF_CODES of arrays of uint8_t, each of length CODE_LENGTH.

I hope that helps.

NB. In answer to your comment about needing to index the array with named indices - there's nothing stopping you from referring to the individual members of codes via an index. What you can do is initialise the whole thing via {0} - shorthand for initialising all members to 0.

And you can assign members of the RADIO_ONOFF array (or any array for that matter) individually e.g.

codes[RADIO_ONOFF][3] = 255;

Try this example - and note the output:

  #include <iostream>

  const int NR_OF_CODES = 4;
  const int RADIO_ONOFF = 0;
  const int CODE_LENGTH = 11;

  const unsigned char RADIO_ONOFF_ARR[] = { 180,99,33,11,22,33,55, 22,22,33, 10};

  int main()
  {
    unsigned char codes[CODE_LENGTH][NR_OF_CODES] = {
                                                   {0},
                                                  };
    std::cout << "Before:\n";

    for(int x = 0; x < CODE_LENGTH; x++)
    {
      std::cout << static_cast<int>(codes[RADIO_ONOFF][x]) << ", ";
    }

    codes[RADIO_ONOFF][3] = 3;

    std::cout << "\nAfter:\n";

    for(int x = 0; x < CODE_LENGTH; x++)
    {
      std::cout << static_cast<int>(codes[RADIO_ONOFF][x]) << ", ";
    }

    // or try memcpy 
    memcpy(codes[RADIO_ONOFF], RADIO_ONOFF_ARR, sizeof RADIO_ONOFF_ARR);

    std::cout << "\nAfter Memcpy:\n";

    for(int x = 0; x < CODE_LENGTH; x++)
    {
      std::cout << static_cast<int>(codes[RADIO_ONOFF][x]) << ", ";
    }

    char c;
    std::cin >> c;
    return 0;
  }
patchwork
  • 1,161
  • 3
  • 8
  • 23
  • Thanks for your very detailed answer! The way you recommended for initialization did not fit for me because I want to be able to index my array with named indizes - like RADIO_ONOFF and so forth. This is a main requirement. This line: codes[RADIO_ONOFF] = new unsigned char[CODE_LENGTH]{ 9,180,88,11,33,4,0,255,64,191 }; – Mike Mitterer Nov 10 '13 at 12:16
  • I thought it declares codes[RADIO_ONOFF] as unsigned char array with CODE_LENGTH length and initializes the array with { values }... in one step... but, yeah, seems I was wrong :-) I probably stay with #4. I think Arduino (lowmem) likes it more... – Mike Mitterer Nov 10 '13 at 12:24
  • You can still reference your array with named indexes, I've edited my answer with an example - however assignment in one go, of the entire array `codes[RADIO_ONOFF]` is more tricky if you're putting it as part of a 2D array. – patchwork Nov 10 '13 at 13:07
  • combining memcpy() and your Solution 4 should be ok. You'll eliminate the need for new - but it really depends on whether that 2d array is local or needs to persist. Anyway - lots of options. – patchwork Nov 10 '13 at 13:18
0

First off all, since this is tagged C++, I'm going to say that there is no need to do #define. You now have access to static const variables that do the same, but are type safe.

Here's what the Google C++ style guide has to say about braced lists:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Braced_Initializer_Lists

I generally follow this guide when I'm not working on a project with an already setup style. Also, I should mention that style is really subjective, so you may end up getting a wide range of answers.

Also, don't use char, where you just want to store byte data. That's what uint8_t is for.

Also, see this SO thread for all your options of how to initialize your arrays: How to initialize all members of an array to the same value? In most of your cases since they are just holding non-changing constants, you can get away with making them static const.

Like this:

static const size_t kMyCodeArraySize = 14;  // an example
static const uint8_t[kMyCodeArraySize] = {1, 2, 3, 4};  // example intialization
Community
  • 1
  • 1
It'sPete
  • 5,083
  • 8
  • 39
  • 72
  • Thanks for pointing me to the Google-Styleguide and your answer. I accepted the above answer (patchwork) because it was a bit more specific for my Q (2 dim + index) - anyways - Thanks! – Mike Mitterer Nov 10 '13 at 12:32