Linked list tree and dynamic pointers.

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?

By "writing to Random Locations in RAM" I meant that you would figure out the RAM address space, and manually set pointers there. For example, say that the SRAM goes from 0x1000 to 0x2000. If you do this: int *ip = (int *)0x1111 you are setting your own pointer to some random spot in RAM ignoring any designs the compiler has for this memory location. It was really a far fetched thing to do on purpose, though it is easy enough to do by accident.

Thanks for clearing that up.

I cant seem to access my 2d array:

class menu
{
public:
  char name[10];
  byte xpos;// position on the screen
  byte ypos;
  byte index[2]; //position in the current menu index
  menu** menulist; //indexed array pointer to group members
  menu* previous_menu; //pointer to previous menu
  float value;

  menu(char *myname="",byte y=0,int x=0)
  {
    strcpy(name, myname);

    menulist = new menu*[x];
    for (byte i=0; i<x; i++)
    { 
      menulist[i] = new menu[y];
    }
  }


};

menu MENU1("menu 1", 2,2);
menu MENU2("menu 2", 2,2);

menu* current_menu=&MENU1;
byte current_index[]={0,0};

void setup(){
  Serial.begin(9600);
  MENU1.menulist[0][0]=&MENU2;
  Serial.println((*current_menu->menulist[0][0]).name);

}

void loop(){

}

no match for 'operator=' in '( MENU1.menu::menulist) = & MENU2'

I was also wondering if it is appropriate to use a NULL pointer as a terminating character for the array or a va_list?

MENU1.menulist[0][0] resolves to an object, not a pointer.

You can only pass it a menu, not menu*

MENU1.menulist[0][0] = MEUN2

Its also the reason why you cannot dereference it in the second line.

I haven't looked at your code thoroughly, however this compiles, but may not be what you want.

  MENU1.menulist[0][0]=MENU2;
  Serial.println((current_menu->menulist[0][0]).name);

If you design your code to look for null, then for sure its a usable idea.

Ah I was going from a one dimensional array to two. If I change it to this then it now looks for a pointer instead (I believe).

class menu
{
public:
  char name[10];
  byte xpos;// position on the screen
  byte ypos;
  byte index[2]; //position in the current menu index
  menu*** menulist; //indexed array pointer to group members
  menu* previous_menu; //pointer to previous menu
  float value;

  menu(char *myname="",int y=0,int x=0)
  {
    strcpy(name, myname);
    xpos=x;
    ypos=y;

    menulist = new menu**[y];
    for (byte i=0; i<y; i++)
    { 
      menulist[i] = new menu*[x];
    }
  }

  ~menu(){
    for(int i = 0; menulist[i]!=0; ++i) {
      delete [] menulist[i];
    }
    delete [] menulist;

  }


};

menu MENU1("menu 1", 2,2);
menu MENU2("menu 2", 2,2);

menu* current_menu=&MENU1;
byte current_index[]={0,0};

void setup(){
  Serial.begin(9600);
  MENU1.menulist[0][0]=&MENU2;
  Serial.println((*current_menu->menulist[0][0]).name);

}

void loop(){

}

I hope I am using delete [] properly, and that any memory that wasn't deallocated resets when you upload a new sketch :blush:.

I am just using the initialization parameters x and y temporarily,
Now I want to shoot for passing a (jagged) list of menu arrays to allocate, each terminated by a NULL pointer. If I am checking for NULL then do I need to pass pointers rather than objects? Is there a way I can pass objects and NULL at the same time? It would be nice not to need to write an & infront of each menu, and assign them such as:

//initialize menu's first

mainmenu1.menulist[][]={
{menu1,menu2,0},
{menu3,menu4,menu5,0},
{menu6,0},
0
}

Or passed as an argument to a function using new to allocate, with the for loop checking for the NULL condition. This is what I tried, is this correct for allocation?:

  void allocate_menus(menu*** list){
    for (int i=0; list[i]!=0; i++){
      int p=0;
      for (p; list[i][p]!=0; p++){
        //menulist[i][p]=list[i][p];   doesnt work here.
      }
      menulist[i] = new menu[p+1]; //allocate space for the null pointer?
    }
  }

So after some reading it seems you cant assign 2d arrays the same way you can initialize them so I can't pass my list of menu's with terminating 0 directly.

Any suggestions for this? Do I need to use a container class, vectors or std::array? Can I assign the 2d array line by line? Can I initialize a temporary array and pass that for allocation/assignment?

Can I assign the 2d array line by line?

2D array is an array of arrays. You need to assign values to elements individually, unless you are doing it when the array is declared (which you aren't).

Ok I am trying the individual assignment, but I am still having some issues with the array structure. Just tried printing the i and p values in the display_menu() function and the values are not what they should be.

This code tries to allocate a menulist[1][1], with size [2][2] because I'm still not sure if I need to allocate space for the terminating 0's. But actually it sets all the allocated pointers to 0 until individual assignment.

class menu
{
public:
  char name[10];
  byte xpos;// position on the screen
  byte ypos;
  byte index[2]; //position in the current menu index
  menu*** menulist; //indexed array pointer to group members
  menu* previous_menu; //pointer to previous menu
  float value;


  menu(char *myname="",byte y=0,byte x=0)
  {
    strcpy(name, myname);
    xpos=0;
    ypos=0;

    menulist = new menu**[y+1];
    menulist[y]=0;
    for (byte i=0; i<y; i++)
    { 
      menulist[i] = new menu*[x+1];
      for (byte p=0; p<=x; p++)
        menulist[i][p]=0;
    }
  }

  ~menu(){
    for(int i = 0; menulist[i]!=0; ++i) {
      delete [] menulist[i];
    }
    delete [] menulist;

  }


};

menu MENU1("menu 1", 1,1);
menu MENU2("menu 2", 0,0);

menu* current_menu=&MENU1;
byte current_index[]={
  0,0};

void setup(){
  
  Serial.begin(9600);
  Serial.println("starting");
  lcd.begin(16,2);
  lcd.print("hello");
  MENU1.menulist[0][0]=&MENU2 ;

  if ((current_menu->menulist[1])==0)
  {
    Serial.println("yes");
  }
  else
    Serial.println("no");
  Serial.println(current_menu->menulist[0][0]->name);
  display_menu();

}

void loop(){

}

void display_menu()
{

  for (int i=0; (current_menu->menulist[i])!=0; i++)
  {
    Serial.print((i));
    for(int p=0; (current_menu->menulist[i][p])!=0; i++)
    {
      Serial.println(p);
      delay(1000);
      lcd.setCursor(current_menu->menulist[i][p]->xpos,current_menu->menulist[i][p]->ypos);
      if (current_menu->menulist[i][p]==current_menu->previous_menu)
      {
        lcd.print("^");
        Serial.println("^");
      }
      else      
        lcd.print(current_menu->menulist[i][p]->name);
    }
  }

}

Serial output:

starting
yes
menu 2
00
20
0
50

confirms that menulist[1] is 0, prints the name of the current menu. Instead of stopping at 00, it seems to skip 1 (instead of ending the loop) and goes to 2, back to 0 (only prints the y index), and then randomly to 5.

I'll have a peek at your code, however in the mean time, you can try this:

Default initialize your arrays, this will zero all of your pointers.

menulist = new menu**[y+1]();

Notice the brackets at the end.

Edit: now I'm confused, did you just delete a post?

Edit 2: It appears the forum is having troubles. Your post disappeared, then my edits were gone. Now they are back.