1

I'm working on an Arduino project where I need to build (and work with) a two-dimensional array at runtime. I've been poking around looking for a solution, but I've had no luck. I found an example of a dynamic one-dimentional array helper here: http://playground.arduino.cc/Code/DynamicArrayHelper, so i've been trying to adopt that code for my use. I created a library using the following code:

My Header file:

#ifndef Dynamic2DArray_h
#define Dynamic2DArray_h

#include "Arduino.h"

class Dynamic2DArray
{
  public:
    Dynamic2DArray( bool sorted );
    //Add an integer pair to the array
    bool add( int v1, int v2);  
    //Clear out (empty) the array
    bool clear();
    //Get the array item in the specified row, column
    int getValue(int row, int col);    
    //Get the number of rows in the array
    int length();    
  private:
    int _rows;
    void * _slots;
    bool _sorted;
    void _sort();
};

#endif

The library's code:

#include "Arduino.h"
#include "Dynamic2DArray.h"

#define ARRAY_COLUMNS 2

int _rows;
void * _slots;
bool _sorted;

Dynamic2DArray::Dynamic2DArray(bool sorted) {
  //Set our local value indicating where we're supposed to
  //sort or not
  _sorted = sorted;

  //Initialize the row count so it starts at zero
  _rows = 0;
}

bool Dynamic2DArray::add( int v1, int v2) {
  //Add the values to the array
  //implementation adapted from http://playground.arduino.cc/Code/DynamicArrayHelper
  //Allocate memory based on the size of the current array rows plus one (the new row)
  int elementSize = sizeof(int) * ARRAY_COLUMNS;
  //calculate how much memory the current array is using
  int currentBufferSize = elementSize * _rows;
  //calculate how much memory the new array will use
  int newBufferSize = elementSize * (_rows + 1);
  //allocate memory for the new array (which should be bigger than the old one)
  void * newArray = malloc ( newBufferSize );
  //Does newArray not point to something (a memory address)?
  if (newArray == 0)   {
    //Then malloc failed, so return false
    return false;
  }
  // copy the data from the old array, to the new array
  for (int idx = 0; idx < currentBufferSize ; idx++)
  {
    ((byte*)newArray)[idx] = ((byte *)_slots)[idx];
  }
  // free the original array
  if (_slots != NULL)
  {
    free(_slots);
  }
  // clear the newly allocated memory space (the new row)
  for (int idx = currentBufferSize; idx < newBufferSize; idx++)
  {
    ((byte *)newArray)[idx] = 0;
  }
  // Store the number of rows the memory is allocated for
  _rows = ++_rows;
  // set the array to the newly created array
  _slots = newArray;
  //Free up the memory used by the new array
  free(newArray);

  //If the array's supposed to be sorted,
  //then sort it
  if (_sorted) {
    _sort();
  }

  // success
  return true;

};

int Dynamic2DArray::length() {
  return _rows;
};

bool Dynamic2DArray::clear() {
  //Free up the memory allocated to the _slots array
  free(_slots);
  //And zero out the row count
  _rows = 0;
};

int Dynamic2DArray::getValue(int row, int col) {
  //do we have a valid row/col?
  if ((row < _rows) && (col < ARRAY_COLUMNS)) {
    //Return the array value at that row/col
    return _slots[row][col];
  } else {
    //No? Then there's nothing we can do here
    return -1;
  }
};

//Sorted probably doesn't matter, I can probably ignore this one
void _sort() {

}

The initial assignment of the _slots value is giving me problems, I don't know how to define it so this code builds. The _slots variable is supposed to point to the dynamic array, but I've got it wrong.

When I try to compile the code into my project's code, I get the following:

Arduino: 1.8.0 (Windows 10), Board: "Pro Trinket 3V/12MHz (USB)"

sketch\Dynamic2DArray.cpp: In member function 'int Dynamic2DArray::getValue(int, int)':

sketch\Dynamic2DArray.cpp:83:22: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]

     return _slots[row][col];

                      ^

Dynamic2DArray.cpp:83: error: 'void*' is not a pointer-to-object type

Can someone please help me fix this code? I've posted the files to https://github.com/johnwargo/Arduino-Dynamic-2D-Array-Lib.

Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
johnwargo
  • 601
  • 2
  • 7
  • 22
  • Does your array actually need to be dynamically-resizeable? – samgak Feb 17 '17 at 03:39
  • 1
    Using *dynamic memory* on an *Arduino* is a **very bad** idea, can you justify it? – Patrick Trentin Feb 17 '17 at 06:35
  • 1
    For your specific error, see [this](http://stackoverflow.com/questions/26755638/warning-pointer-of-type-void-used-in-arithmetic) and the more general explanation [here](http://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c) – Patrick Trentin Feb 17 '17 at 08:56
  • 1
    In your case you correct the compile error by defining _slots as `int ** _slots;`. – Arno Bozo Feb 17 '17 at 10:51
  • Yes, the array has to be dynamic because I won't know until runtime how many entries the array will contain. I know it's not a good idea, but I have no other choice. Every morning, the application builds a list of actions it needs to take, and that list can vary day to day. Is there some other way I should be doing this? Except for a linked list (which I didn't want to try to accomplish on the Arduino), dynamic array seemed like the right approach. – johnwargo Feb 17 '17 at 12:53
  • I know you don't like the answer, but I do agree with the solution proposed by @ArnoBozo, specifically because you are less limited with that approach. His suggestion to use `int ** _slots` should also solve the compilation error you have. Is there anything else that can be added here? – Patrick Trentin Feb 18 '17 at 17:16
  • Changing the code fixed that compilation problem, but others quickly appeared, so the code's apparently crap. I'll drop this approach. – johnwargo Feb 20 '17 at 00:55

1 Answers1

1

The code you took was for a 1D dynamic array; the modifications for a 2D array are too tricky. Give up these horrors.

I think there is no reason you use dynamic array. You can assume that size max is ROW_MAX * COL_MAX, so you can define a static array int array[ROW_MAX][COL_MAX].

  • on one hand if you defined a dynamic array, you could free space when you dont use it anymore and take advantage of it for other work. I dont know if this is your case.
  • on the other hand if you define a static array (on UNO), you have 32kB available on program space, instead of 2kB available on RAM.

Because of the difference 32kB / 2kB, there are very few chances you can get bigger array with dynamic allocation.

Arno Bozo
  • 454
  • 3
  • 8
  • I know that I took code for a one dimensional array and I'm trying to covert it to cover a two-dimensional array. That's the whole premise of my question. Why do you feel the need to tell me something I already know? I have 30 years of experience as a professional software developer, and I've thought through all of the options for what I want to do here and this is the approach I've selected. Can we please stop spending so much effort telling me this is a bad idea and simply help me answer the question I've asked? I'm not asking if this is a good idea, I'm asking for help doing this. – johnwargo Feb 18 '17 at 02:50
  • 1
    Patrick helped you as much as we can do here. In your question and comment you explicitly said that you were looking for a workaround to solve your problem. C/C++ is unable to create dynamic multidimensional array and there is no good workaround for arduino, that is not obvious at all, that is what we have to point out on this topic. You are far from completing a code that nobody should ever read on internet (only because it is not generic). So we can not help you more on stackoverflow. Sorry, happy coding. – Arno Bozo Feb 18 '17 at 15:22
  • OK, I'm sorry, but I didn't see anyone say "C/C++ is unable to create dynamic multidimensional array" if that's true, then that's the answer I needed. What I was seeing mostly was people questioning my approach and outright saying I was stupid to attempt it, which wasn't helpful. Sometimes you just want to do something complicated to learn something new - in this case, I wasn't happy about the approach, but I was at least interested in a solution (to see if I could do it) I don't understand what you mean by "You are far from completing a code that nobody should ever read on internet" – johnwargo Feb 20 '17 at 00:44
  • In C it is possible to create a static array like `int my2D[n][p]`. It is possible to create dynamically something that is used with the syntax `myNotArray[i][j]` but they are not 2D array, they are `int ** myNotArray`. In particular the data are not contiguous. With C++ there is no dynamic multidimensional array either. But you can design objects that look like. – Arno Bozo Feb 20 '17 at 15:41
  • We did not say you were writing stupid code. But your program has specific needs that may lead to intricate code. In arduino architecture we try and respect memory constraints, so we cannot write good and generic code. Hence nobody on internet can take advantage of it. If I know at compile time that my array will be of type `int`, then I should not use DynamicArrayHelper, and I should not use your code. This is not documented on internet, it is misleading. At last in your code there are several errors, and you dont have written all your methods, so your are far from finishing. Good luck. – Arno Bozo Feb 20 '17 at 16:12