Derive array variable from 2d array (matrix) and/or array of arrays(jagged)

Hello world of Arduino!

I am relatively new to Arduino, but am an old crusty curmudgeon with a background in programming. I have been out of the coding game for awhile and seemed to be stumped by what should be simple. I am usually pretty self-sufficient in finding the solutions and have gone through a number of threads about this topic, but all seem to only contain bits and pieces, and I cant seem to put it together. Appreciate any insight into my problem:

I will keep this a simple as possible.

I get this using a matrix

void setup() {
  
  Serial.begin(9600);
  int matrix[2][3] =
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };
  
  int *ptr = matrix[0]; // pointer to the first element of the array
  
  for ( int i = 0; i < 2*3; i++ )
   // print the value pointed to, and increase pointer
   Serial.println( *ptr++ );
}

Prints
11
22
33
44
55
66

For me the first element of matrix would be {11,22,26}, not 11. So can I derive a variable from matrix that would basically represent a row:

int row[3] = {11,22,26};
or
int row[3] = matrix[0]; //pseudo, does not work

basically I want to process the 2d array row by row in another array of row values.

Which leads me to the other approach I tried with an array of arrays, which I am more familiar with, but seem to have more problems with.

void setup() {
  Serial.begin(9600);  
   
  int data_1[3] = {11, 22, 33};
  int data_2[3] = { 44, 55, 66 };
  int* data[2] = { data_1, data_2 };
  
(pseudo)
  // how do I do something like this
  int record[3] = data[0];
  //this should be array of ints representing first row, I get dreaded int to*int conversion error here
  
  // print the row values
  Serial.println(record[0] + "," + record[1] + "," record[2]);
  
  
}

I want this to print:
11,22,33

This is what I really need to do, as I will want a jagged array eventually, but for this purpose, I cant seem to wrap my head around getting that row, from either type of array into a variable. I have tried many variations on this snipet and know I am missing something with the pointer. Finally gave up and decided to ask for help. Its late as well.

Disclaimer: I have significant background in C#, therefore I take pointers for granted and hence the nature of my pseudo code :slight_smile: I do understand the basic principles of pointers and memory address concepts. I just just don’t seem able to apply them here. :confused:

Any insight is greatly appreciated. Thanks!

Computer nerds would probably say you should create classes for matrix and row (vector) objects, then to write member functions within the classes to do want such as row-by-row processing and printing.

(Above is based on my limited knowledge of C++, not C#. I am not sure what Arduino IDE is capable of.)

I am not sure what Arduino IDE is capable of

As it uses C++ as its programming language then with a few exceptions if you can do it in C++ then you can do it in the Arduino IDE.

Your matrix, as you defined it, will consist of 6 ints.

You set your variable “ptr” to point to the first entry in the matrix.

You then attempt to print the next six ints.

I don’t know what you would expect, but your first example is doing exactly what I would expect it to do.

Your matrix is defined as two arrays, with three elements each. That is 6 ints occupying 12 consecutive bytes.

If you want to get a pointer which points to the first row of the matrix, you could do this:

int matrix[2][3] =
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };

int* row ;

for ( int i=0 ; i<2 ; i++ )
{
     row=&(matrix[i][0]) ;

     for ( int j=0 ; j<3 ; j++ )
     {
           Serial.println( row[j] );
     }
}

In that code above, row is not creating any kind of additional data, it is still pointing to the same data.

If you want to create a copy of a row of the data, you could do this:

int row[3] ;

// To copy the i'th row of matrix,   where i is either 0 or 1
int i=1 ;
memcpy( row, matrix[i], 3*sizeof(int) );

In the second piece of code from your original post:

// how do I do something like this
int record[3] = data[0];
//this should be array of ints representing first row, I get dreaded int to*int conversion error here

No. data is an array of two pointers, as you defined it a few lines before ( I also dubious if that definition of data_1, data_2, and data will work as you have described it. ).

But assuming the definition of data has worked, then data[0] will be a pointer, the address of the first element of data_1

Then you attempt to initialise and assign a value to an array of three ints, and try to put one pointer value into it. That can’t possibly work.

int record[3] ;
memcpy ( record, data[0], 3*sizeof(int)) ;

You can use the assignment operator ( that is, = ) in C to assign a value of a basic data type, or a struct, or a pointer, but you cannot use it to copy a whole array.

Arrays in C are closely related to pointers, so you can do something like this:

  int matrix[2][3] =    // matrix is conceptually array of pointers to arrays of int
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };

  int * row0 = matrix[0] ;   // row0 is pointer to array of int
  int * row1 = matrix[1] ;   // row1 is pointer to array of int

  Serial.println (row0 [2]) ;  // same as matrix[0][2]

In reality matrix just points to a 6 element array of int, and singly
indexing it constructs pointers into the middle of this appropriately.

Thus the compiler behind the scenes is working out the amount to
adjust pointers to point to the correct row. Because this is all
compile-time, you cannot have variable dimension matrices in C/C++
as the compiler has to know the structure.

Well I think that's how it works. Often people do explicit indexing
calculations themselves, which means you can generalise to dynamically
dimensioned arrays.

Well I think that's how it works.

It is if you treat an array name as a pointer (and dereference to reach the nested arrays). And initialization allows you to layout a multi-dimensional array using a single dimension initializer.

If you just need to use the sub-array, not copy it, and need more information than a pointer, then use a reference:

int matrix[2][3] =
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };


int (&matrixEl)[ 3 ] = matrix[ 0 ];

matrixEl is an array reference to a nested array in matrix, which works correctly with sizeof and ranged for loops.

Hey Everyone, Wow! Post at bedtime and wake up to feedback! Love it. Thanks everyone.

I need to get some time to sift through the responses. I just wanted to clarify a few thing from my original post.

In my first code example using matrix, where I say " I get this using a matrix", I meant that I "understand" this example and know it works. Sorry for the misunderstanding.

And yes, both these do work. at least they compile.

  int data_1[3] = {11, 22, 33};
  int data_2[3] = { 44, 55, 66 };
  int* data[2] = { data_1, data_2 };

  int moredata[2] = { (11, 22, 33), (44, 55, 66) };

But why does one require a pointer, what is the difference in these two declarations?

Once I get a chance to get some working examples I will post an update.

Thanks again for the feedback all!

Salud!

Wake up? 1:30 in the afternoon here, you must be a “bit” farther west.

Don’t need int if all your data is <=255, just use byte. 1 int = 2 bytes, saves on SRAM.

int moredata[2] = { (11, 22, 33), (44, 55, 66) };

This is wrong!

moredata only contains two elements and is equivalent to: int moredata[2] = { 33, 66 };.

What will work is this: int moredata[][3] = { 11, 22, 33, 44, 55, 66 };

But why does one require a pointer, what is the difference in these two declarations? int* data[2] = { data_1, data_2 };

You cannot have arrays of references, just references to arrays. If this is what you need, your original design is what you want.

int matrix[2][3] =
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };

whenever 'matrix' is evaluated as a value, it's type is 'pointer to an array of 3 ints' and its value is the memory address where the value is located

int (*matrix_p)[3] = matrix;

whenever matrix or matrix_p get a number added to or subtracted from them, the addition and subtraction is multiplied by 6, because 6 is the side of 'array of three ints'

int (*matrix_1)[3] = matrix + 1;
int (*matrix__p1)[3] = matrix_p + 1;

you can check this by casting 'matrix_1' to a long integer and looking at the value, although the bytes might be in a weird order

Whenever you dereference matrix or matrix_p, the result is of type 'array of 3 ints'. Whenever this is evaluated as a value, the type of the value is 'pointer to int' and the value will be a memory address.

int * matrix_deref = *matrix;
int * matrix_p_deref = *matrix_p;

To get to the second array element, therefore, you add 1 to matrix or matrix_p and then dereference

int * matrix_1_deref = *(matrix+1);
int * matrix_p_1_deref = *(matrix_p + 1);

C provides some syntactic sugar for "add a number to a pointer then dereference it" - the array operator:

int * matrix_1_deref = matrix[1];
int * matrix_p_1_deref = matrix_p[1];

Your code gets a pointer to the first element of the first array. You then illegally increment this beyond the end of that array. This works ok because C does not do bounds checking and because of the way that the compiler lays out arrays in memory in this particular case.

It might fail if the particular C compiler you are using were to - for instance - always pad out arrays to an even number.

So hey all!

and CrossRoads, this stuff has been keeping me up late…

Thanks again for the all the feedback. It really got me kinda going a bit. I was sitting here reviewing and kept on seeing over and over, “If you can do it in C/C++, you can do it in Arduino”. I glance over at my book shelf and notice a dusty old copy of “The C Primer”! Classic!!!

So, I dust it off and give it an old peruse. Especially the sections on pointers. After all that, I don’t know if I understood better, or had just confused myself more.

Anyway, I was able to put together what I think is the start of a nice sample that really draws out what I was originally looking for. At this point I am well past needing what I originally wanted. I have a sketch I am working on that will use what I have learned. I’ll followup with that when its ready…

I did not include any of the memcopy approaches, yet, as I do not need it. See my TODO list in the sample header.

In the meantime, here is my preliminary sample, all dressed up. I still have some issues and still don’t understand what is going on in Part 2- approach 2. This should compile and run in setup().

And one other thing, I am with Archibald. And I am that kind of nerd. something from C# like IList, ICollection and IDictionary would be great, but I do understand the need to whittle Arduino development down to bare required basics. Those C# collection libraries can be large.

Also, C/C++ foundation of Arduino!!!??? Why did I get these errors when something crashed in the IDE:

at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)…etc, etc, etc…

The IDE does look like Java?! Is it opensource as well? When I get my chops back I plan to have a look at the Arduino source, just to blow my mind.

If anyone is interested, review the sample and possibly offer insight into my outstanding issues. They are identified pretty clear in the code.

Here ya go, should just be able to copy, paste, compile and run. Enjoy!

//////////////////////////////////////////////
// "Almost All About Arrays, kinda..."
// compiled by SwartHack 5/19/2015
// contributers to my effort: All of Arduino Forum community and specifically:
// pYro_65, CrossRoads, MarkT, michinyon, UKHeliBob and Archibald
//
// TODO - convert to using byte instead of int.
// TODO - add a 3d example, try a 4d and blow your mind
// TODO - Nest, Nest, Nest where we can. Show examples of nesting and variable elimination.
// TODO - add memcopy examples, or what I know as pseudo cloning.
// TODO - Anything else?

void setup() {
  Serial.begin(9600);
  
  //use this to store/format messages
  char msg[80];
  
  /***************************************************************************
  Part 1 - 2d array
  ****************************************************************************
  start with a standard 2d array, also known as a matrix. Lets visualize it
  this way using a table analogy, just like a spreadsheet. 
  So we will have a heirarchy of {table, row, column, cell}. Also frequently
  used is the graphics display analogy of {screen, column, row and pixel}. Also serious bit heads
  could understand the memory analogy, but that is beyond me
  ******************************************************************************/
  sprintf(msg,"Part 1 - 2d array processing...\0");
  Serial.println( msg );
  
  int table[2][3] =
  {
   { 11, 22, 33 },
   { 44, 55, 66 }
  };
  
  // or the same thing would also be
  // int table[2][3] = {11, 22, 33, 44, 55, 66 };
  
  // pointer to the first element of the array
  // which is actually the cell matrix[0][0], or 11
  int *row = table[0]; 
  
  // print the value pointed to, and increment pointer
  // to cycle through the entire array in order
  for ( int i = 0; i < sizeof(table) / sizeof(int); i++ )
  {
    int cell = row[i]; //*row++; //row[0];
    sprintf(msg,"value: %i",cell);
    Serial.println( msg );
  }
  
  
  // now lets get a reference to the first row and print it
  // this defines a pointer to the array of values in the row.
  int * row1 = table[0] ;
  int * row2 = table[1];
 
  
  sprintf(msg, "\nvalues of row1: %i , %i, %i\0", row1[0],row1[1],row1[2]);
  Serial.println (msg) ;
  sprintf(msg, "values of row2: %i , %i, %i\n\0", row2[0],row2[1],row2[2]);
  Serial.println (msg) ;
  
  /* TODO use nested for loops to do the same thing TODO */
  
  
  //////////////////////////////////////////////////////////////
  // OK, this is fun!!!
  ///////////////////////////////////////////////////////////////
  
  /*****************************************************************************
    Part 2 - Array of Arrays
    ****************************************************************************
    So this can be similar, if not the same as working with a 2d array. But there 
    can be significant differences as well. This can be illustrated using a 
    dataprocessing analogy like {data, record, element}. When we get data
    we know we have records and elements, but we also know they may not be the 
    same size,let alone type. You can also nest up to the nth level dimension 
    (memory limited). For example, in a cell value you have an array of hex 
    values, etc. It tends to get mind bending a  little and is a topic to
    explore for a more advanced example. but for know just think Lawnmower Man.
    For know lets keep it real with this example
    *****************************************************************************
  */
  
  /* 
   * Approach 1 - create an int array from two int arrays the end result being a [3][] array ????
   *
   * Issue 1 -- I have to declare it in peices!!??? why? See Approach 2
   *
   * Issue 2 -- The sizeof does not seem to be returning the desired result!!??
   *
  */
  sprintf(msg,"Part 2 - Array of Arrays, approach 1...\0");
  Serial.println( msg );
  
  
  int record_1[3] = {11, 22, 33};
  int record_2[2] = { 44, 55 };
  int *data[2] = { record_1, record_2 };
  
  int * record1 = data[0];
  sprintf(msg, "Record 1 has: %i elements(s)\0", sizeof(*record1) / sizeof(int));
  Serial.println (msg) ;
  sprintf(msg, "elements of record 1: %i , %i, %i\n\0", record1[0],record1[1],record1[2]);
  Serial.println (msg) ;
  
  int * record2 = data[1];
  sprintf(msg, "Record 2 has: %i element(s)\0", sizeof(record2) / sizeof(int));
  Serial.println (msg) ;
  sprintf(msg,"elements of record 2: %i, %i\n\0", record2[0], record2[1]);
  Serial.println (msg) ;
  
  /* TODO - figure out sizeof so we can process in nested for loops  - TODO */
 
  ///////////////
  // want a unreported runtime error, try this
  /////////////////
  //sprintf(msg, "Value of record2: %i , %i, %i\n", record_2[0],record_2[1],record_2[2]);
  //sprintf(msg, "Value of record2: %i", record2[0]);
  
  
  /* 
   * Approach 2 - Initialize/define the array all in one go
   *
   * Issue -- 1. Seems you have to use () to define the inner integer array elements
   *             otherwise you get some scalar conversion error. Not quite sure on 
   *             that one?!
   *
   * Issue -- 2. Does not seem to work as expected, but it is working. Is some operation
   *             happening here when using the ()
  */
  sprintf(msg,"Part 2 - Array of Arrays, approach 2...\0");
  Serial.println( msg );
  
  int moredata[2] = { (11, 22, 33), (44, 55) };
  
  int rec_1 = moredata[0];
  int rec_2 = moredata[1];
  
  sprintf(msg, "Rec 1 has: %i elements\0", sizeof(rec_1) / sizeof(int));
  Serial.println (msg) ;
  sprintf(msg, "elements of Rec 1: %i\0", rec_1);  //returns 33
  Serial.println (msg) ;
  sprintf(msg, "Rec 2 has: %i elements\0", sizeof(rec_2) / sizeof(int));
  Serial.println (msg) ;
  sprintf(msg, "elements of Rec 2: %i\0", rec_2);  //returns 55
  Serial.println (msg) ;
  
  Serial.end();
  
  
}

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

}

Oh, man, just saw PaulMurrayCBR response. His insight is not included in my first example version. Need to review and will apply accordingly.