How to initialize (or Declare) a multidimensional array

I consider it a bit of a shortcoming of the Reference section on the website in that it doesn't expand upon the Array section on how to declare a multidimensional array. Worse yet, after spending nearly two hours going through forum posts, it isn't well explained there either. (There are a couple of hints, though)

Rather than leave it at that, I've decided to create this thread to make a definitive place to look for assistance on this topic.

For instance, you're trying to declare a multidimensional array of X integers in Y rows (or a 2 dimension array).

You'll find a lot of code when searching that suggests you to use a for loop to feed in the data like this:

int Matrix[Y][X];
for (int i =0; i < Y; i++) {
  for (int j =0; j < X; j++) {
    Matrix[i][j] = some data;
  }
}

But, in some cases, you already know what that data is and you'll search the array section and wonder, does the Arduino not properly handle initializing multidimensional arrays? It does, even if it is a bit quirky.

Here's how you do it:

int Matrix[][X] = { {1,2,3,4,...,X},
                    {3,4,5,6,...,X},
                    {1,9,2,8,...,X},
                    {9,8,7,6,...,X} };

The things that are important to note are:

  • The compiler does not seem to care about how many rows you have in the array, it cares about how many items (or columns) in each row
  • Declaring the number of rows always causes an error (not altogether sure why)
  • Not declaring how many items in each row will give you this cryptic error: "multidimensional array must have bounds for all dimensions except the first"
  • Increasing the number of dimensions will make your memory usage grow exponentially!
  • This is one of two ways that I have gotten the compiler to compile without giving errors, the second method uses #define for each row

HTH :wink:

4 Likes

Worse yet, after spending nearly two hours going through forum posts, it isn't well explained there either.

Get a basic C book. It IS explained there.

But, in some cases, you already know what that data is and you'll search the array section and wonder, does the Arduino not properly handle initializing multidimensional arrays? It does, even if it is a bit quirky.

There's nothing quirky about how the Arduino handles multi-dimensional array declarations. It is EXACTLY the same as how C does it.

The compiler does not seem to care about how many rows you have in the array, it cares about how many items (or columns) in each row

It most certainly does care about the number of rows. That's why it is counting them.

Declaring the number of rows always causes an error (not altogether sure why)

Not if you get the number right.

Not declaring how many items in each row will give you this cryptic error: "multidimensional array must have bounds for all dimensions except the first"

There is nothing cryptic about that message. If it said something like "Bats fly at night, under a full moon", that could be considered cryptic.

Increasing the number of dimensions will make your memory usage grow exponentially!

No. The growth will be linear.

the second method uses #define for each row

Which is absolutely the wrong way.

Paul,

This is the second time you've trolled one of my posts. After reviewing your past posts prior to my logging back on, it appears you get your jollies from being negative and unhelpful. Perhaps if you spent as much time being constructively helpful instead, people would be able to find the assistance they seek.

All that being said, I am not disregarding that some of your points are valid, just that you've wasted my time (and probably all the people who might be looking for an answer) instead of offering useful alternatives. Go away and waste someone else's time!

8 Likes

LTU1:
Increasing the number of dimensions will make your memory usage grow exponentially!

Since each added dimension basically IS an exponent, this isn't too surprising. If you have a 2x3 array, and make it into a 2x3x5 array, The usage goes from 6 to 30. Well, maybe "exponent" is the wrong word. "Multiplier" is more accurate. It shouldn't grow exponentially, but for larger data sets, especially for smaller array sizes (like 2 or 3) it may certainly appear exponential from a strictly number-browsing perspective.

Thanks for the summary on arrays though. As you've discovered, that information is scattered and a pain to find and gather up if you need to look it all up after not having used it for awhile. Well worth bookmarking. My brain doesn't absorb material well in description form. I get a lot more value from examples like yours.

Hi LTU1,

this might make your thread to go off topic, and that is a shame, because I think you have made a good positiv attempt to contribute. Thumbs up.

However:

LTU1:
Paul,

This is the second time you've trolled one of my posts. After reviewing your past posts prior to my logging back on, it appears you get your jollies from being negative and unhelpful.

PaulS is always obtuse and sarcastisc in his answers because he hopes to get people to THINK about their problems instead of just whining "it dont work - do it for me". As he is correct in his observations and comments, I for one consider it more helpful than not.

Leaving the choice of wording to one side at the moment you have factual errors (as in fact - proved by reading manual or doing experiments): There is no exponential growth - the array grows with exactly the size you specify (which is mutiplying each dimension as virtual1 points out), all elements and rows are counted (how else would it know how much memory to use?) and there is only an error if your count and the compilers count in number of lines differ - and the compiler is always right in this case. (thats what computers do best; count :slight_smile: )

You use emotive / anthropomorphism to express your opinion on how the compiler works. Of course you are entiltled to your opinion - and PaulS is thus allowed to voice the opposite.

There are several of shortcommings and indeed a few errors in the reference and tutorials. There has been forum debate about this, but the Arduino team has choosen not to enlist help from here. Also the Arduino team has made some decisions on how much of the language C++ (which is what Arduino code is) or C (which is almost a subset of C++) to ignore in their explanations, in order to present a "simple" language.

Now, lets get the thread back on the subject, and hope some persons finds the information and finds it helpfull. I suggest you make a new post with the factual corrections (rather than edit the top post as this makes the succeeding posts confusing).

I don't care for Paul's sarcasm when I am honestly offering help. I understand using sarcasm when someone is wrong, however, I won't be sarcastic to those seeking help. If he knows of a better way, I am open to constructive assistance. When the first post I ever get from Paul is to look at "blink without delay" for a bit of coding assistance when that was clearly not the issue, I tend to disregard his assistance because it isn't assistance. If he had told me to use "return" instead of "break" command, it would have been constructive assistance. Instead, he skimmed my post and reflexively jumped on my use of delay(). I leave it for you to judge.

His post here is worse. To see that he knows that I am giving misinformation and then does not provide the correct information, well, my time has been wasted because I didn't get to learn the correct way from someone who knows. (And that doesn't count the people who might read this thread.)

WHICH IS THE ENTIRE POINT OF COMING HERE FOR HELP.

Excuse me for shouting.

It seems that everyone above this post has been debating my observation that multiple variables will cause memory usage to be exponential. I see it this way:

An X by Y array where X=Y has X^2 memory allocated
An array with X, Y, and Z where X=Y=Z yields X^3 memory allocated

What Paul is saying is wrong is if X, Y, and Z are not equal to each other, it is not exponential, but can still be very large. Which is why I said I didn't discount what he said. Regardless, large arrays on an UNO is not recommended (see the specs for why).

The last C/C++ compiler I used on a regular basis was Borland's venerable Turbo C compiler along with gcc nearly a decade ago. I have no doubts that some things have since changed or become deprecated. I base my observations about the Arduino compiler's behavior on that. When I declared arrays back then, it was expected of the programmer to explicitly enter all dimensions of a multidimensional array or matrix. My basic C book is even older.

For what it's worth, I tested the #define method and it works without issues. Since Paul says it's the wrong way, I won't confuse anyone regarding that by sharing it, though I still await his "right" way of doing it. I carefully read what Arduino team has shared regarding const and #define before considering the idea. Ironically, it was by their written suggestion to discuss corrections to the reference that brought me to posting a thread about it.

Even with all these side discussions, I am still thinking that this thread is more useful in that it allows everyone (including myself) to learn the "right" way from (when the correct solutions are given) what I may have incorrectly shared.

2 Likes

iirc I read somewhere recently that using const (or was it static, or some special variant of const) instead of #define gives you a very slight reduction in compile size and/or sram usage. something about the variable getting copied at runtime or some such detail.

virtual1:
iirc I read somewhere recently that using const (or was it static, or some special variant of const) instead of #define gives you a very slight reduction in compile size and/or sram usage. something about the variable getting copied at runtime or some such detail.

According to the Arduino Reference, #define is copied at compilation. They also make a distinct preference to using const instead for arrays. The reason given for such a preference is to avoid unwanted side effects which they don't mention what those side effects might be. The manner in which I am using the #define is for a look up table and I haven't witnessed unwanted side effects other than those due to my mistakes in coding.

That being said, they fail to give any examples in the array section on how to declare arrays with const. I am far more familiar with #define than I am with const, so I chose that instead. Perhaps I'll try and figure it out in the future.

And just after I clicked post on the above post, I remembered how const is used.

const int Array[][2] = {{1,2},{3,4},{5,6},...,{X,Y}};

I'll stick with #define, thank you very much. :stuck_out_tongue:

And a side note: A link to my C reference book which is now online!

PaulS is right. The response is blunt, but it is correct. I hear your frustration, trying to contribute, but then being bluntly corrected. But you and anyone who wants to succeed will do well to take criticism in stride, especially when it is accurate.

Keep contributing, but don't resist good peer review and beware the #define!

@Msquare, LTU1:

I don't agree PaulS was being sarcastic.

There's nothing quirky about how the Arduino handles multi-dimensional array declarations. It is EXACTLY the same as how C does it.

It most certainly does care about the number of rows. That's why it is counting them.

Declaring the number of rows always causes an error (not altogether sure why)

Not if you get the number right.

These are facts. I don't see that they are being put in a "bitterly cutting or caustic" way.

... does the Arduino not properly handle initializing multidimensional arrays? It does, even if it is a bit quirky.

Comments like these tend to get our backs up. As explained in the "sticky" at the top of Programming part of the forum, the Arduino uses C++. It is not an "Arduino" language, that somehow fails to handle arrays, due to some sort of incompetence.


PaulS gets his stuff right 99.9% of the time. He encourages you to think for yourself, rather than just posting code "oh, look, here is how to do it".

I think his motto is to teach you how to fish, rather than just handing you a fish to eat, today.

The reason given for such a preference is to avoid unwanted side effects which they don't mention what those side effects might be.

Since macros do literal source-level replacement, you will tend to get side-effects if you pass an argument to one, like "a++".

Example:

https://www.securecoding.cert.org/confluence/display/seccode/PRE31-C.+Avoid+side+effects+in+arguments+to+unsafe+macros

There are subtle issues here, and you may code for a year without noticing side-effects and be lulled into thinking that they therefore can't happen.

Personally I recommend const, unless it simply can't be used, because of token-substitution or some of the more esoteric aspects of the compiler pre-processor.

I've decided to create this thread to make a definitive place to look for assistance on this topic.

Thanks for contributing to the knowledge-base that is this forum. Those of use who have done so in the past have had their initial efforts corrected and improved, and I trust you will take these comments in the spirit in which they are intended.

I encountered bidimensional arrays in a piece of code and trying to understand how they work I stumbled on this post, so I have aquestion for you guys.
As a multidimensional array in (in my case bidimensional) can be declared as:
type name [3] [4] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
or
type name [3] [4] {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
or
type name [3] [4] {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
In which case I need to use a multidimensional array instead of a normal array with the same number of elements?
i.e. type name [12] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
BTW multidimensional arrays do work with Arduino :wink:

[code]

/*
 * http://www.tutorialspoint.com/cprogramming/c_multi_dimensional_arrays.htm
 * The simplest form of multidimensional array is the two-dimensional array.
 * A two-dimensional array is, in essence, a list of one-dimensional arrays.
 * To declare a two-dimensional integer array of size [x][y], you would write
 * something as follows −
 * type arrayName [ x ][ y ];
 * Where type can be any valid C data type and arrayName will be a valid C identifier.
 * A two-dimensional array can be considered as a table
 * which will have x number of rows and y number of columns.
 */
int biDimArray [4] [5] { //initialize to zero
  {0, 0, 0, 0, 0},       //do not forget the ,
  {0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0}
};


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println();
  Serial.println("Print Array Before Overwriting The Content");
  int a, b;
  for (a = 0; a < 4; a++) {
    for (b = 0; b < 5; b++) {
      Serial.print(biDimArray [a] [b]);
      Serial.print(' ');
    }
    Serial.println();
  }
  for (a = 0; a < 4; a++) {
    for (b = 0; b < 5; b++) {
      biDimArray [a] [b] = analogRead (A0);
      delay(15);
    }
  }
  Serial.println();
  Serial.println("Print Array After Overwriting The Content");
  for (a = 0; a < 4; a++) {
    for (b = 0; b < 5; b++) {
      Serial.print(biDimArray [a] [b]);
      Serial.print(' ');
    }
    Serial.println();
  }
  Serial.println();
  /*expected result:
   * Print Array Before Overwriting The Content
   *0 0 0 0 0
   *0 0 0 0 0
   *0 0 0 0 0
   *0 0 0 0 0
   *
   *Print Array After Overwriting The Content
   *478 474 469 472 475 (random values)
   *473 468 470 474 471
   *466 467 472 470 464
   *464 469 468 462 462
   *
   */
}

void loop() {
  // put your main code here, to run repeatedly:

}

[/code]

1 Like

Well looks like a multi dimensional array could be an Array of strings (https://www.arduino.cc/en/Reference/String) but I still cannot see the practical advantage...

While there are a number of ways in which you can initialise the array, the advantages of using a multi-dimensional array over a single dimensional array are really to do with how you think about the contents of the array when you are programming your code.

It doesn't matter much when the arrays are trivial, like in the examples that are typically given, but when you get to large arrays it reduces the errors in referencing the arrays.

Think of a 2 dimensional array as a chess board. You could start at the top and work your way to square 64 or you could look at row 8, column 8. That's how Excel does it and its 16k wide by 1m deep.

In 3D arrays, think of it as a transparent cube.

With higher numbers of dimensions, think of it as an array of cubes, or a 2D array of cubes or a cube of cubes. It gets properly complex to imagine after that... :slight_smile:

But using a multi dimensional array makes it much simpler to find the correct cell again.

I don't want to get this thread too far off topic, but since this thread seems to have two topics and I have a comment that relates to both, so even though it's an old thread, I'll add the comments

LTU1 has some valid points about PaulS: PaulS has made some incorrect comments; Memory usage is NOT Linear when adding dimension to an array and even though there are couple comments to the contrary. Every dimension added to the array does use memory exponentially. I'll explain (we'll use #'s from the original source): first array declaration = [2] for a total of 2, second declaration = [3] for a total now of 6 --> [2][3] = 6. Then we add a third dimension = [5] for a new total of 30 --> [2][3][5] = 30. We have to make up a 4th dimension and it should start to become clear; 4th = [4] for a total of 120 --> [2][3][5][4] = 120. The total IS figuratively increasing exponentially, in that the slope of the curve is getting steeper with each added dimension. If you don't believe it: put the totals; 2, 6, 30, 120 in a spreadsheet and graph them.

PaulS this is NOT linear, which would mean that the graph goes in a straight line; not a curved line. When I read PaulS post, I also thought he was being rude and I read Msquare come to his defense in a following post, but I don't care what his excuses are, if you are being rude, you are not being helpful. Everybody is a beginner when they start and how encouraging is it to be rude to someone because they don't do it the way you think it should be done? Well, I'll tell you, it's not encouraging and besides rude, it's arrogant. BTW: PaulS, there is nothing cryptic about "Bats fly at night, under a full moon" where else would you expect to find them; even your attempt at being a smart-alec was incorrect.

2 Likes

You are getting confused. Try this test program:

byte a [2];
byte b [2][2];
byte c [2][2][2];

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  Serial.print ("Size of a = ");
  Serial.println (sizeof a);
  Serial.print ("Size of b = ");
  Serial.println (sizeof b);
  Serial.print ("Size of c = ");
  Serial.println (sizeof c);
  
  }  // end of setup

void loop () { }

Output:

Size of a = 2
Size of b = 4
Size of c = 8

That's linear! Each array is the size of the previous one multiplied by the new number of elements.

There is no exponentiation there.

for a total of 120 --> [2][3][5][4] = 120

That's right. 2 * 3 * 5 * 4 = 120. There is nothing being raised to a power. Of course if you plot the numbers it will be a curve because you are choosing larger numbers.

Try making them all size 1:

byte a [1];
byte b [1][1];
byte c [1][1][1];

The result is an array of one byte in all 3 cases.


I suppose you could argue that this is exponential in a sense:

byte a [2];
byte b [2][2];
byte c [2][2][2];

The resulting size is going to be 2n where n is the number of dimensions.

On the other hand, I could argue that given an array:

byte foo [666];

And if I add another dimension like this:

byte foo [666] [2];

Now the size has doubled (666 * 2). I wouldn't really say that adding a dimension gives you exponential growth. I suppose it is an argument about words to an extent.

And of course if I do this:

byte foo [666] [1];

There is no growth at all.

there is nothing cryptic about "Bats fly at night, under a full moon" where else would you expect to find them;

This phrase would certainly be cryptic if it were a compiler error message, which was the point.

I followed this thread and still I don't understand what this statement here does. Can anybody in the community explain it to me?

byte addresses[][6] = {"1Node"}; // Create address for 1 pipe. *)

Thanks a lot.

John

*) taken from: https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-ExampleSketches)

1 Like

Johncoffee:
byte addresses[][6] = {"1Node"}; // Create address for 1 pipe. *)

You have a two-dimensional array. The first (left) dimension has an unspecified number of elements. Since there is one initializer in the braces, then it has one element. So it is the same as:

byte addresses[1][6] = {"1Node"};

The other dimension is a 6-element character array. In this particular case it will hold "1Node\0" where \0 is the null-terminator (so, 5 bytes of "1Node" plus 1 byte of 0x00).