Reputation: 52519
This works, I know.
int8_t intArray7[][2] = {-2, -1,
0, 1,
2, 3,
4, 5,
6, 7};
int8_t intArray8[][2] = {10, 9,
8, 7,
6, 5,
4, 3,
2, 1,
0, -1,
-2, -3};
int8_t intArray9[][2] = {100, 101,
102, 103};
int8_t (*arrayOf2DArrays[])[2] = {intArray7, intArray8, intArray9};
Now, what if the 2D arrays are not all the same width? How do I make that work?
Ex: this does NOT work, since intArray11 is width 3:
int8_t intArray10[][2] = {-2, -1,
0, 1,
2, 3,
4, 5,
6, 7};
int8_t intArray11[][3] = {10, 9, 8,
7, 6, 5,
4, 3, 2,
1, 0, -1};
int8_t intArray12[][2] = {100, 101,
102, 103};
int8_t (*arrayOf2DArrays2[])[] = {intArray10, intArray11, intArray12};
What must one do to make an array of arrays of different element sizes?
I plan on keeping track of each of the array sizes using separate variables.
Upvotes: 0
Views: 111
Reputation: 52519
Alright, I'd like to answer my own question too. There are many good ideas here, but let me post this simple and low-memory-cost solution. For devices of minimal memory (ex: an ATTiny AVR microcontroller with 512 bytes of RAM and a few kB program flash memory), I think this may be the best solution. I've tested it; it works. Use an array of pointers to int8_t's--ie: an array of pointers to 1D arrays, as follows:
int8_t intArray10[][2] = {-2, -1,
0, 1,
2, 3,
4, 5,
6, 7};
int8_t intArray11[][3] = {10, 9, 8,
7, 6, 5,
4, 3, 2,
1, 0, -1};
int8_t intArray12[][2] = {100, 101,
102, 103};
//get some array specs we need about the arrays
byte arrayLengths[] = {sizeof(intArray10)/sizeof(int8_t), sizeof(intArray11)/sizeof(int8_t),
sizeof(intArray12)/sizeof(int8_t)}; //redundant division since int8_t is 1 byte, but I want to leave it for verboseness and extension to other types
byte arrayCols[] = {2, 3, 2};
//make pointers to int8_t's and point each pointer to the first element of each 2D array, as though each 2D array was a 1D array
//-this works because the arrays use contiguous memory
int8_t *intArray10_p = &(intArray10[0][0]);
int8_t *intArray11_p = &(intArray11[0][0]);
int8_t *intArray12_p = &(intArray12[0][0]);
//make an array of those pointers to 1D arrays
int8_t *intArrayOfnDimArray[] = {intArray10_p, intArray11_p, intArray12_p};
//print the 3 arrays via pointers to (contiguous) 1D arrays:
//-Note: this concept and technique should work with ANY contiguous array of ANY number of dimensions
for (byte i=0; i<sizeof(intArrayOfnDimArray)/sizeof(intArrayOfnDimArray[0]); i++) //for each 2D array
{
for (byte j=0; j<arrayLengths[i]; j++) //for all elements of each 2D array
{
Serial.print(intArrayOfnDimArray[i][j]); Serial.print("/"); //now read out the 2D array values (which are contiguous in memory) one at a time; STANDARD ARRAY ACCESS TECHNIQUE
Serial.print(*(intArrayOfnDimArray[i] + j)); Serial.print("/"); //ARRAY/POINTER TECHNIQUE (extra teaching moment)
Serial.print((*(intArrayOfnDimArray + i))[j]); Serial.print("/"); //POINTER/ARRAY TECHNIQUE
Serial.print(*(*(intArrayOfnDimArray + i) + j)); //POINTER/POINTER TECHNIQUE
//add a comma after every element except the last one on each row, to present it in 2D array form
static byte colCount = 0; //initialize as being on "Column 1" (0-indexed)
if (colCount != arrayCols[i]-1) //if not on the last column number
Serial.print(", ");
else //colCount==arrayCols[i]-1 //if we *are* on the last column number
Serial.println();
colCount++;
if (colCount==arrayCols[i])
colCount = 0; //reset
}
Serial.println(F("-----")); //spacer
}
Output:
-2/-2/-2/-2, -1/-1/-1/-1
0/0/0/0, 1/1/1/1
2/2/2/2, 3/3/3/3
4/4/4/4, 5/5/5/5
6/6/6/6, 7/7/7/7
-----
10/10/10/10, 9/9/9/9, 8/8/8/8
7/7/7/7, 6/6/6/6, 5/5/5/5
4/4/4/4, 3/3/3/3, 2/2/2/2
1/1/1/1, 0/0/0/0, -1/-1/-1/-1
-----
100/100/100/100, 101/101/101/101
102/102/102/102, 103/103/103/103
-----
Note: as Thomas Matthews teaches in his answer, to access a specific 2D array, row, and column, use the following:
byte index = row * maximum_columns + column;
which in my above example would be:
byte rowColIndex = desiredRow * arrayCols[desired2DArrayIndex] + desiredColumn;
int8_t val = intArrayOfnDimArray[desired2DArrayIndex][rowColIndex];
Note: byte
is equivalent to uint8_t
.
Upvotes: 0
Reputation: 57678
I believe the most compatible method is to use a struct
(or class
) to describe the array:
struct Array_Attributes
{
int8_t * array_memory;
unsigned int maximum_rows;
unsigned int maximum_columns;
};
You can treat the array_memory
as a multidimensional array by converting from 2d to 1d indices:
unsigned index = row * maximum_columns + column;
You could also make an array out of linked lists. This would allow dimensions of different sizes.
+-------+ +----------+ +----------+
| row 0 | --> | column 0 | --> | column 1 | ...
+-------+ +----------+ +----------+
|
|
V
+-------+ +----------+ +----------+
| row 1 | --> | column 0 | --> | column 1 | ...
+-------+ +----------+ +----------+
Upvotes: 1
Reputation: 8945
"Kindly stop and visualize, for a moment," what "the memory" actually looks like . . .
arrayOf2DArrays2
, apparently, consists of: "an array of three pointers."
Unfortunately-for-you, there is nothing, in any of this, to predict what each of these three elements consist of, and we can very-plainly see that all three of them are different. "But, C++ ... cannot!" (Because: you have so-far provided no means for it to do so.)
Therefore, you now need to avail yourself of C++'s "container classes."
arrayOf2DArrays2
ought to consist of "a container," presently containing (three) other "containers," each one of which is capable of telling you how many elements it contains, and also of throwing a runtime-exception should you attempt to access an element that it does not contain.
When you have done this, a "simple" statement such as arrayOf2DArrays2[x][y]
(or what-have-you ...) will "automagically" be understood by C++ to refer to an actually-complex series of events: first, the outer-container will be asked to resolve [x]
, then the thus-referenced inner-container will be asked to resolve [y]
.
Upvotes: 0