Linked list tree and dynamic pointers.

I am trying to create a menu with adafruits 16x2 RGB LCD keypad shield (5 buttons). I have heard of "linked lists", but would like to access multiple menu's from one screen, instead of a linear list. The max number of menu's per screen can be 6, 3 on each row. My question is if I use a multidimensional array of size [2][3], is it bad to just set them all to NULL and only use the ones I need? I have heard of issues with dynamic allocation of arrays in structs/classes due to lack of "new" and have no experience with MALLOC.

Here is my class so far:

class menu
{
   public:
   char name[10];
   unsigned int xpos;// position on the screen
   unsigned int ypos;
   menu* menulist[2][3]; //array pointer to group members
   menu* previous_menu; //pointer to previous menu
   float value;
   
   menu(char *myname,int x1,int y1)
   {
   strcpy(name, myname);
   xpos=x1;
   ypos=y1;
   }
   
};

I have heard of issues with dynamic allocation of arrays in structs/classes due to lack of "new"

The Arduino now has a new function.

is it bad to just set them all to NULL and only use the ones I need?

Of course not.

How would I go about generating the array of pointers dynamically?

I know it better practice to make variables private, but in this case I cannot see why limiting the scope of the variables provides any benefit. I want to be able to set the lcd cursor to current_menu.menulist[i][p].xpos etc.. Otherwise I have to write a seperate function for each get_name, get_pos, get menulist, get value etc... Am I missing something?

Why do many people choose to avoid using SRAM for an array of pointers in a class such as this? What is the difference between MALLOC and PROGMEM and what is actually being stored? The address's of the pointers or something else?

I have a mega so much more memory, is it still worth saving the SRAM? Doesn't saving and retrieving information from the Flash memory take more time? Especially if it is constantly being displayed on the LCD?

A lot of questions there, I'll respond to one or two of them

What is the difference between MALLOC and PROGMEM and what is actually being stored?

Malloc() is a function that "grabs" a section of SRAM for you, PROGMEM is a macro that forces data to stay in flash memory and not get copied to SRAM.

When you malloc() some SRAM memory you get a pointer to that memory, what you put in there is up to you.

Doesn't saving and retrieving information from the Flash memory take more time?

Yes, but not enough to worry you in this case I think, the LCD is a 1000x slower anyway.

But if you have static data in flash memory why would you need to dynamically allocate SRAM with malloc()? Does the data change at run time?


Rob

Graynomad:
A lot of questions there, I'll respond to one or two of them

Malloc() is a function that "grabs" a section of SRAM for you, PROGMEM is a macro that forces data to stay in flash memory and not get copied to SRAM.

When you malloc() some SRAM memory you get a pointer to that memory, what you put in there is up to you.

Yes, but not enough to worry you in this case I think, the LCD is a 1000x slower anyway.

But if you have static data in flash memory why would you need to dynamically allocate SRAM with malloc()? Does the data change at run time?

I don't have data in flash memory yet, I'm trying to figure out if I need it and if so, how to go about it. Is it if you never change the variable being pointed at, and only read it, then its a viable option for PROGMEM in this case?

As for MALLOC. Why do you need to "grab" a section of SRAM? Isn't it available anyway? Or is it to prevent it being overwritten by other variables?

The problem is that at least one menu will have 6 items, but the rest are likely to have 2 or so. I need help initializing a multidimensional array in a class of max size [2][3].

I can't do:

int size;
 menu* menulist[2][size];

So how do I do it? Or I don't need to? Just seems a waste to have that [2][3] declaration if im only using the max size once.

The compiler wants to know where everything in SRAM is located. You are free to create static buffers, or use malloc(), but you won't get very far if you create a pointer to a random location in SRAM and start putting data there.

So if I create menu objects then assign the pointers stored in them to other menu objects without using Malloc then they will eventually get overwritten?

PaulS:
The Arduino now has a new function.

So new and delete are now automatically included in 1.05-r2?

The only pointer that changes is the current_menu pointer, which is supposed to point to a menu object, and access the other object(s) being pointed to by that object (instances?). The objects pointed to by the array itself don't change but some of the variables inside them might.

They don't need to change at runtime, but how can I initialize different size arrays for different objects of the same/derived class?

but how can I initialize different size arrays for different objects of the same/derived class?

malloc() or new.

How are you allocating these menu objects? As long as you don't assign them to random locations in SRAM, you are fine.

thanks for your solution

KeithRB:
How are you allocating these menu objects? As long as you don't assign them to random locations in SRAM, you are fine.

Does the new keyword solve this problem?

A static buffer is fine, too.

class menu{
  
  public:
  char name[10];
  byte xpos;
  byte ypos;
 // menu** menulist;
  menu* previous_menu;
  double value;
  
    menu(char *mname, byte y, byte x){
      
      menu** menulist = new menu*[y];
      for (byte i=0; i<y; i++)
      { 
        menulist**[i] = new menu*[x];
      }
      
    strcpy(name, mname);
  }
  
 /* ~menu()
  {
    delete [] menulist;
  }*/
};

I am having trouble with the line menulist**[y] = new menu*[x]; . It keeps saying: expected primary-expression before'[' token.

If I call a different size [x] for each of [y] can each array in y be a different size?

Ok so they need to be constructed on allocation.. Since my class has pointers to its own members at the moment this wont really work. How can I initialise the pointers to menu objects without constructing them first? I thought it only allocates the memory without assigning it first "its up to me what I put in there".

Ideally I'd like to initiate all the menu objects/pointers and then do something like:

EC_PH.menulist[][]={
{this->menu_previous, EC, PH},
{DOSING, CALIBRATION}
}

where the menu index[0][0] is for the previous menu

That would be a neat way of showing all the menu's inside a menu, and also give an idea of their layout.

class menu{
  
  public:
  char name[10];
  byte xpos;
  byte ypos;
  //char menuname[10];
  menu** menulist;
  menu* previous_menu;
  double value;
  
    menu(char *mname="", byte y=0, byte x=0){
      
      menulist = new menu*[y];
      
    strcpy(name, mname);
  }
  
 /* ~menu()
  {
    delete [] menulist;
  }*/
};

menu EC_PH("EC PH",1,3);
menu EC("EC ONLY",0,0);

menu* current_menu=&EC_PH;

void setup(){
  Serial.begin(9800);
  Serial.println("STARTING");
  EC_PH.menulist[6]=&EC;
  Serial.println((*current_menu->menulist[6]).name);
}

void loop(){
}

Why does it allow me to assign and dereference an array in position 6 in allocated memory when the array is supposed to be of size 1? Is this just overwriting memory which hasent been dynamically allocated?

Should I even be using dynamic memory? The size of menulist for each member of the menu class does not change at runtime, but the size is different for each menu. But now I'm scared of "pointing to random locations in SRAM", but equally scared of memory leaks due to my inexperience with dynamic memory.

Also how can you have a "static buffer". A buffer is used to store data temporarily, and static variables are not supposed to change. Seems contradictory.

I still don't know what happens if I allocate an array of pointers of size x, and then try to use the array index x+y. From what I understand new allocates some memory in SRAM from the heap, so that no other variables can use it. So again I ask as above, if I try to use memory outside of the allocated space, is this simply using memory which could be overwritten by other variables?

Any help with these questions or the others I've asked would be appreciated. I don't know if I'm asking dumb questions or what the reason is that no one will help me, but its frustrating since I have nothing to do but code, yet I can't get anything done.

static has a few different meanings in C++. I does not imply const however.

Why does it allow me to assign and dereference an array in position 6 in allocated memory when the array is supposed to be of size 1? Is this just overwriting memory which hasent been dynamically allocated?

Because its valid code. It might not be correct, however it compiles.
C++ is intended to run alongside exceptions. If you accessed an array out of bounds, an exception would be thrown.
ATmel decided that exceptions incur too much overhead and left them out of the standard setup.

So we are left with C++'s default behavior of 'who cares, if it ain't broke...' As there is valid memory there to be read, ownership of it is a conceptual idea to manage the memory ( prevent it from being corrupted ), but there is nothing forcing you to abide by it.

pYro_65:
Because its valid code. It might not be correct, however it compiles.
C++ is intended to run alongside exceptions. If you accessed an array out of bounds, an exception would be thrown.
ATmel decided that exceptions incur too much overhead and left them out of the standard setup.

So we are left with C++'s default behavior of 'who cares, if it ain't broke...' As there is valid memory there to be read, ownership of it is a conceptual idea to manage the memory ( prevent it from being corrupted ), but there is nothing forcing you to abide by it.

Not only does it compile, it returns the expected value. I assume this is because it hasn't had a chance to be corrupted (overwritten?) yet. This along with what you said seems to confirm what I said earlier, that the index goes out of bounds to non-allocated memory. I can understand the concept of it being the coder's responsibility to manage the memory exceptions but this begs the question what is the difference between dynamically allocated memory and not?

If its only that allocated memory cannot be accidentally overwritten by other variables, then why aren't pointers allocated memory by default? And if they are, why is there a danger to "assigning pointers to random locations in SRAM"? Especially if the object being pointed to doesn't change.

In other words, with the above logic, shouldn't pointers always be allocated to avoid them being overwritten?

Maybe you could clarify "pointers to random locations in SRAM"?

Maybe you could clarify "pointers to random locations in SRAM"?

Well, that is just undefined behavior, What you need to remember also is, AVR maps its registers into the address space, so I can do this:

volatile char *c = ( volatile char* )0x05;
c[ 0 ] = 0xFF;

This is actually writing directly to PORTB, rather than RAM. This may be very bad as PB6 and PB7 ( bits ) are actually the pins connected to the crystal.

Check out my FAQ entry, it starts by explaining the major differences between 'static' and dynamic memory.
http://arduino.land/FAQ/content/4/26/en/how-to-use-dynamic-memory.html

Thank you. Your FAQ is very informative and well explained, albeit information I have already read.

On a side note, I think I understand the difference between static variables and const now, but still not sure how a "static buffer" would work. Are you saying have a static variable inside the menu class that increments a size variable every time a new menu is allocated to that menu object?

I am still a little hazy on the "writing to random locations in SRAM" though. Are you saying if I decide not to bother with dynamic lists and do this:

class menu
{
   public:
   char name[10];
   unsigned int xpos;// position on the screen
   unsigned int ypos;
   menu* menulist[2][3]; //array pointer to group members
   menu* previous_menu; //pointer to previous menu
   float value;
   
   menu(char *myname="",int x1=0,int y1=0)
   {
   strcpy(name, myname);
   xpos=x1;
   ypos=y1;
   }

};

menu MENU1;
menu MENU2;

void setup(){

MENU1.menulist[0][0]=&MENU2;

}

void loop(){
  
}

That this is somehow bad? Or were you only referring to dynamic allocation?