Go Down

Topic: Storing an array pointer in an array! (Read 3127 times) previous topic - next topic

PeterH

To start with, I would question your grounds for wanting to use the String class. Perhaps it's necessary to achieve what you're trying to achieve, but perhaps it isn't. The String class is IMO quite dangerous and not wise to use at all if it can possibly be avoided - and I would take quite a lot of convincing that it can't be avoided here. That said, ...

If you want to hold a pointer-to-thing in an array, then the array needs to be declared as an array of pointer-to-thing.

Code: [Select]

int one;
int two;
int *myArray[] = { &one, &two };


It's not clear that you actually need to use an array of pointers. Are you trying to pass in a set of String objects? If the set of Strings in the set is fixed (although the content of each String may vary) can't you just define an array of String? Maybe what you're trying is sensible and it's just that you've simplified your example so far that the sense is no longer apparent, but it may also be that the solution you're trying to force to work is not actually necessary.

If you're going to pass this array to a function and expect the function to process each element in the array then you will also need to provide some way for the function to know how many elements are in the array. That could be via you knowing and hard-coding the length, or the calling code passing in another argument giving the length, or having some way to determine the length of the array being passed in (sometimes it's appropriate to pass in a null-terminated array of pointers) but one way or another there needs to be some way to know the length.
I only provide help via the forum - please do not contact me for private consultancy.

TanHadron

Here's an example of syntax for one way to do arrays of C type strings.

Code: [Select]

char cStringEmpty[] = "";
char cStringOne[] = "one";
char cStringTwo[] = "two";
char cStringThree[] = "three";
char cStringFourScoreAndSevenYearsAgo[] = "Four score and seven years ago,";

char *arrayOne[] = {cStringOne, cStringThree, cStringTwo};
char *arrayTwo[4];

void setup()
{
  arrayTwo[0] = cStringFourScoreAndSevenYearsAgo;
  arrayTwo[1] = cStringEmpty;
  arrayTwo[2] = cStringOne;
  arrayTwo[3] = "Testing, Testing, 1..2..3..";
  Serial.begin(9600);
}

void loop()
{
  Serial.println("Printing arrayOne");
  PrintArray(arrayOne, 3);
  Serial.println("Printing arrayTwo");
  PrintArray(arrayTwo, 4);
  for(;;);
}

void PrintArray(char **stringArray, int arraySize)
{
  int i;

  for (i = 0; i < arraySize; ++i)
  {
    Serial.print("stringArray[");
    Serial.print(i);
    Serial.print("] = ");
    Serial.println(stringArray[i]);
  }
}


Output:
Code: [Select]

Printing arrayOne
stringArray[0] = one
stringArray[1] = three
stringArray[2] = two
Printing arrayTwo
stringArray[0] = Four score and seven years ago,
stringArray[1] =
stringArray[2] = one
stringArray[3] = Testing, Testing, 1..2..3..


Really, your C strings are quite flexible.  You can use hard coded strings, or strings read in or generated.  Your string array is just an array of character pointers.  But they have to point to valid places in memory where there are characters (or not), followed by a null terminator.  The null terminator is mandatory.  The empty string simply points to a null terminator.  The strings don't have to be of uniform size, and you can allocate them or move them around if you need to.  Also, as you can see, you can have multiple string arrays that have elements that point to some of the same strings.  The strings are not duplicated in memory, but the pointers are.

cowasaki

I do appreciate the help but the thread does go off in the direction of whether I need to do what I am doing.  The demo program has nothing to do with the final code which needs to use Strings and cannot be hard coded.

The application in question is the DueGUI I am writing and basically the programmer will add objects like this:

Code: [Select]

progressbar=DueGUI.addProgressBar(375,100,40,30,60,50,ss,1,clrBlue,clrWhite,clrBlack,clrRed,2,optVisible,URNProgressbar);
progressbar2=DueGUI.addProgressBar(20,100,40,350,60,30,ss,0,clrGreen,clrWhite,clrBlack,clrYellow,2,optVisible,URNProgressbar2);
DueGUI.addButton(100,425,200,50,clrBlue,clrWhite,clrWhite,clrRed,clrWhite,2,"Refresh",posCentre,posCentre,BVS_28,optVisible ,URNRefresh);
btnAdd=DueGUI.addButton(100,250,200,50,clrCyan,clrWhite,clrBlack,clrRed,clrWhite,2,"Bring back 1&2",posCentre,posCentre,BVS_28,optInvisible,URNAdd);
btnRemove=DueGUI.addButton(100 ,350,200,50,clrGreen,clrWhite ,clrBlack,clrRed,clrWhite,2,"Remove  1&2",posCentre,posCentre,BVS_28,optVisible,URNRemove);
DueGUI.addButton(350,350,199,50,clrBlue ,clrWhite ,clrWhite,clrRed,clrWhite,2,"Fan Screen",posCentre,posCentre,BVS_28,optVisible,URNFanScreen);
DueGUI.addButton(600,350,199,50,clrBlue ,clrWhite ,clrWhite,clrRed,clrWhite,2,"Clock Screen",posCentre,posCentre,BVS_28,optVisible,URNClockScreen);


The library then stores the added objects which can be buttons, images, graphical shapes, text, input boxes, analogue clocks etc etc in a master block of arrays but the arrays are general purpose so as to save memory.

What I am trying to do is allow the user to pass a function:

addCycleStringButton(word x,word y,word xs,word ys,long colour,long borcolour,long textcolour,long presscolour,long presstextcolour,byte borwidth,String top,word xo,word yo,int font,int initialstate,String* elements,int cyclexo,int options,bool visible,int URN);
 
Where the variable passed in red is an array of elements for a drop down list.

The addCycleStringButton function will then store the address of the array in an array and later the draw/redraw/update functions will recall this address and access the array to find the relevant Strings in order to draw the object on the screen.

The test program simplifies this to the extreme that it makes no sense as to WHY I am doing it but I just wanted to make it simple so that I wasn't having to post massive amounts of code.

As it stands at the moment the test program does exactly what I need up to the point of USING that stored address....

Code: [Select]
String test1[10];
String test2[10];
String *stored[100];
int numarrays=0;

void setup() {
test1[0]="1"; test1[1]="2"; test1[2]="3";
test2[0]="one"; test2[1]="two"; test2[2]="three";
Serial.begin(115200);

  testpassarray(test1);
  testpassarray(test2);

  reprint(0);
 
}

void loop() {
}

void testpassarray(String *elements){
stored[numarrays]=elements;   
numarrays+=1;
Serial.println(elements[0]);
Serial.println(elements[1]);
Serial.println(elements[2]);
}

void reprint(int whicharray){


// setup variable "elements"         //  HOW ????
 
// elements= address of which array  //  HOW ????

  int objectNumber=0;
  while (element[objectNumber]!=""){
    Serial.println(element[objectNumber]);
    objectNumber+=1;   
  }


}



So now the whole program works other than the final function....

on entry to the final function we have the following:

int whicharray - this contains the location in the store[] array of the address of the String array
stored[]  - this contains the stored pointers
the number of elements is specified by an empty element so I'm using a while loop

SO....

How do I make "element[]" point to the correct array now that I can recall it's address from the store[] array?

as I said I do appreciate the help but it has to be done using Strings and if there is a way to do what I am wanting to do it will actually make it much simpler.

Nick Gammon

Code: [Select]

void setup() {
test1[0]="1"; test1[1]="2"; test1[2]="3";
test2[0]="one"; test2[1]="two"; test2[2]="three";
Serial.begin(115200);

  testpassarray(test1);
  testpassarray(test2);

  reprint(0);
 
}



You are passing local variables to a function, which immediately go out of scope when loop ends. Thus your desire to store a pointer to them is misguided. I suggest you look up "variable scope". Also look up "malloc" and "free", or "new" and "delete".

You don't have to use the String class, you can malloc an amount of memory to hold a string of the exact size you need.

If the array is being passed purely temporarily you might want to look into passing by reference.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

cowasaki

#19
May 27, 2013, 02:34 pm Last Edit: May 27, 2013, 02:37 pm by cowasaki Reason: 1

Code: [Select]

void setup() {
test1[0]="1"; test1[1]="2"; test1[2]="3";
test2[0]="one"; test2[1]="two"; test2[2]="three";
Serial.begin(115200);

 testpassarray(test1);
 testpassarray(test2);

 reprint(0);
 
}



You are passing local variables to a function, which immediately go out of scope when loop ends. Thus your desire to store a pointer to them is misguided. I suggest you look up "variable scope". Also look up "malloc" and "free", or "new" and "delete".

You don't have to use the String class, you can malloc an amount of memory to hold a string of the exact size you need.

If the array is being passed purely temporarily you might want to look into passing by reference.





The array is setup outside of the function setup ie in the main code.  Does that not make it global !

I am writing a library, I do not know how big the Strings will be before hand.  They can be 0-200 characters and there can be up to 100 sets mainly empty.  Strings are what I am using.


This is as far as I've got:

Code: [Select]


// Global variables

String test1[10] = {"1","2","3"};
String test2[10] = {"one","two","three"};
String *stored[100];
int numarrays=0;

void setup() {
 Serial.begin(115200);
 testpassarray(test1);
 testpassarray(test2);
 reprint(0);
 reprint(1);
}

void loop() {
}

void testpassarray(String *elements){
 stored[numarrays]=elements;  
 numarrays+=1;
 Serial.println("");
 Serial.println((unsigned long int)elements,HEX);
 Serial.println(elements[0]);
 Serial.println(elements[1]);
 Serial.println(elements[2]);
}

void reprint(int whicharray){
 
 String elements[10]; ////  This line needs to setup "elements" to point to the String array referenced by stored[whicharray]

 Serial.println("");
 Serial.println("");
 Serial.println((unsigned long int)stored[whicharray],HEX);
 
 int objectNumber=0;
 while (elements[objectNumber]!=""){
   Serial.print(objectNumber);
   Serial.print(" = <");
   Serial.println(elements[objectNumber]);
   Serial.println(">");
   objectNumber+=1;    
 }

 // release any variables etc

}

cowasaki

#20
May 27, 2013, 02:45 pm Last Edit: May 27, 2013, 02:48 pm by cowasaki Reason: 1
OK, just realised what you are saying and that the array is created as a local variable and passed rather than the address.....

So how can I pass the address of the global variable to the store function

AND

How do I access the Strings of the global array whilst inside either function when I have the address?

I tried:

testpassarray(&test1);

but it doesn't like it.....

PaulS

Quote
OK, just realised what you are saying and that the array is created as a local variable and passed rather than the address.....

That's true here:
Code: [Select]
void setup() {
test1[0]="1"; test1[1]="2"; test1[2]="3";
test2[0]="one"; test2[1]="two"; test2[2]="three";
Serial.begin(115200);

  testpassarray(test1);


It's not true here:
Code: [Select]
String test1[10] = {"1","2","3"};
String test2[10] = {"one","two","three"};
String *stored[100];
int numarrays=0;

void setup() {
  Serial.begin(115200);
  testpassarray(test1);


The first code pass a pointer to the local array to the function.
The second code passes a pointer to the global array to the function.

Quote
So how can I pass the address of the global variable to the store function

Use the second code, there the array variable IS a global variable.

Quote
How do I access the Strings of the global array whilst inside either function when I have the address?

Just like you are doing in the second code.

cowasaki


Quote
OK, just realised what you are saying and that the array is created as a local variable and passed rather than the address.....

That's true here:
Code: [Select]
void setup() {
test1[0]="1"; test1[1]="2"; test1[2]="3";
test2[0]="one"; test2[1]="two"; test2[2]="three";
Serial.begin(115200);

  testpassarray(test1);


It's not true here:
Code: [Select]
String test1[10] = {"1","2","3"};
String test2[10] = {"one","two","three"};
String *stored[100];
int numarrays=0;

void setup() {
  Serial.begin(115200);
  testpassarray(test1);


The first code pass a pointer to the local array to the function.
The second code passes a pointer to the global array to the function.

Quote
So how can I pass the address of the global variable to the store function

Use the second code, there the array variable IS a global variable.

Quote
How do I access the Strings of the global array whilst inside either function when I have the address?

Just like you are doing in the second code.



I don't understand how version 1 and version 2 are not the same.  Both of the arrays are created globally I just stick the contents into the already created array in the original version and stick them in at the creation time in the second one.

1) So how do I pass the address of the string array to the function?
2) If I have the String array address how do I access the array itself?

PaulS

#23
May 27, 2013, 03:06 pm Last Edit: May 27, 2013, 03:08 pm by PaulS Reason: 1
Quote
1) So how do I pass the address of the string array to the function?

You don't have a string array. You have a String array. The differences are crucial. The address of the array and the address of the first element in the array are, by definition, the same thing. You can use either &test1[ 0 ] or simply test1.

Quote
2) If I have the String array address how do I access the array itself?

Serial.println(test1[ 0 ]);
Serial.println(test1[ 1 ]);
etc.

When you store the pointer to a one dimensional array in a one dimensional array, you are creating a 2D array.

elements[ 0 ][ 0 ] is the same as test1[ 0 ] if elements[ 0 ] is pointing to test1.

PeterH


What I am trying to do is allow the user to pass a function:

addCycleStringButton(word x,word y,word xs,word ys,long colour,long borcolour,long textcolour,long presscolour,long presstextcolour,byte borwidth,String top,word xo,word yo,int font,int initialstate,String* elements,int cyclexo,int options,bool visible,int URN);
 
Where the variable passed in red is an array of elements for a drop down list.


You need to provide your function with a way to know how many items are in the array. From what you have said so far, your code does not know this already so the caller needs to prove the information in some form. The two options I can see are (1) add an argument to receive the number of items in the array, or (2) require that the array have some special value such as a null pointer in the last position that your library code can detect.

You also need to decide whether you are going to create a copy of the array that was passed in, or merely remember the pointer you received. If you only store the pointer then you rely on the caller retaining the array intact for as long as you need it and this introduces the risk of some nastybugs if the caller gets it wrong; if you take copy the array, obviously this needs extra memory.

Implementing any of these approaches is simple enough once you have decided which approach you want to take.
I only provide help via the forum - please do not contact me for private consultancy.

cowasaki



What I am trying to do is allow the user to pass a function:

addCycleStringButton(word x,word y,word xs,word ys,long colour,long borcolour,long textcolour,long presscolour,long presstextcolour,byte borwidth,String top,word xo,word yo,int font,int initialstate,String* elements,int cyclexo,int options,bool visible,int URN);
 
Where the variable passed in red is an array of elements for a drop down list.


You need to provide your function with a way to know how many items are in the array. From what you have said so far, your code does not know this already so the caller needs to prove the information in some form. The two options I can see are (1) add an argument to receive the number of items in the array, or (2) require that the array have some special value such as a null pointer in the last position that your library code can detect.

You also need to decide whether you are going to create a copy of the array that was passed in, or merely remember the pointer you received. If you only store the pointer then you rely on the caller retaining the array intact for as long as you need it and this introduces the risk of some nastybugs if the caller gets it wrong; if you take copy the array, obviously this needs extra memory.

Implementing any of these approaches is simple enough once you have decided which approach you want to take.


Thanks,  as per the example I'm using a null entry to signify the last entry.

I will read the array in the add function and store the number of entries.

cowasaki


Quote
1) So how do I pass the address of the string array to the function?

You don't have a string array. You have a String array. The differences are crucial. The address of the array and the address of the first element in the array are, by definition, the same thing. You can use either &test1[ 0 ] or simply test1.

Quote
2) If I have the String array address how do I access the array itself?

Serial.println(test1[ 0 ]);
Serial.println(test1[ 1 ]);
etc.

When you store the pointer to a one dimensional array in a one dimensional array, you are creating a 2D array.

elements[ 0 ][ 0 ] is the same as test1[ 0 ] if elements[ 0 ] is pointing to test1.


Thanks, "strings" was a typo, you've pulled me up on that one before :D.  I am only looking at Strings here.....

I have tried calling the function with testpassarray(&test1[]);  and   testpassarray(&test1);

I get an error due to the type in the function....  What type should it be to receive the pointer and how do I implement it?


void testpassarray( <what type> elements){
  stored[numarrays]=elements;   // store the address in the stored[] array as a pointer to the actual array
  numarrays+=1;
  Serial.println("");
  Serial.println((unsigned long int)elements,HEX);   // was used to show the address but can be ignored
//  Serial.println(elements[0]);
//  Serial.println(elements[1]);
//  Serial.println(elements[2]);

}

and crucially once that is done how do I use the address in stored[] to actually pull the data from the original global array?

cowasaki

I really do appreciate all your help in this matter.  The test program was created just to find the answers.  I couldn't really use the actual program as it is 7000 lines of code spread across multiple files.

once I understand this using the test program I can transfer that understanding to the actual library....


PaulS

Quote
I have tried calling the function with testpassarray(&test1[]);  and   testpassarray(&test1);

Neither of which is right. It is either:
Code: [Select]
testpassarray(&test1[0]);
or
Code: [Select]
testpassarray(test1);
Both values are the same, although, to me, the second is a lot easier to see as meaning the whole array.

Quote
void testpassarray( <what type> elements){

<what type> is String *.

PaulS

Quote
and crucially once that is done how do I use the address in stored[] to actually pull the data from the original global array?

The array, stored, is a collection of pointers to arrays. That makes it a 2D array, as I mentioned earlier.

stored[ 0 ][ 0 ] is a pointer to the first element of the first array. stored[ 0 ][ 1 ] is a pointer to the second element of the first array. stored[ 1 ][ 0 ] is a pointer to the first element of the second array.

Go Up