Rearrangement/Assignment of large char arrays, without copying (pointer arrays)

Hello everybody,
I am trying to use a very efficient way of rearranging large char arrays, without actually copying those arrays. Therefore my approach was to use pointers.
I am having a hard time understanding the use of pointers, because I couldn't find a good tutorial for creating an array of pointers to an array of char arrays.
Below an example of how I would rearrange pointers and assign new values.

 char** ErrorC; // array of pointer (double pointer)

  *ErrorC[0] =  "test1"; // assign the first value to a pointer 0
  *ErrorC[1] =  "test2"; // assign the second value to a pointer 1

  Serial.println(*ErrorC[0]); // print the the value of  the pointer 0
  Serial.println(*ErrorC[1]); // print the the value of  the pointer 1
  Serial.println(*ErrorC[2]); // print the the value of  the pointer 2 (nothing assigned yet)

  ErrorC[2] =  ErrorC[0]; //rearrange just addresses
  *ErrorC[1] =  "test3"; // assign a new value to a pointer 1

  Serial.println(*ErrorC[1]); // print the the value of  the pointer 1
  Serial.println(*ErrorC[2]);// print the the value of  the pointer 2

Is that actually the right approach? Should I go completely in a different direction?
How would you do it?

This is not an array of pointer char** ErrorC; // array of pointer (double pointer)this is just a pointer to a pointer to a char, there is no array memory reserved.

So those two entries in the array (if you were using precedence correctly)

  *ErrorC[0] =  "test1"; // assign the first value to a pointer 0
  *ErrorC[1] =  "test2"; // assign the second value to a pointer 1

do not exist. There is no space to store those pointers

That’s an example for how you could assign pointer to const char arrays

const char * arrayOfConstCharPointers[] = {
  "Entry #1",
  "Entry #2",
  "Entry #3"
};

The variable ErrorC is not an array; it holds a pointer to a variable that holds another pointer to a character / character array.

And no worries, I also battle with it when it comes to pointers to pointers.

I suggest that you crank up the warning levels in File -> Preferences to 'All'. The result below

Compiling sketch...
"C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR   "-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino" "-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\standard" "C:\Users\sterretje\AppData\Local\Temp\arduino_build_576061\sketch\sketch_dec28a.ino.cpp" -o "C:\Users\sterretje\AppData\Local\Temp\arduino_build_576061\sketch\sketch_dec28a.ino.cpp.o"
C:\Users\sterretje\AppData\Local\Temp\arduino_modified_sketch_519183\sketch_dec28a.ino: In function 'void setup()':

C:\Users\sterretje\AppData\Local\Temp\arduino_modified_sketch_519183\sketch_dec28a.ino:4:14: warning: invalid conversion from 'const char*' to 'char' [-fpermissive]

   *ErrorC[0] =  "test1"; // assign the first value to a pointer 0

              ^

C:\Users\sterretje\AppData\Local\Temp\arduino_modified_sketch_519183\sketch_dec28a.ino:5:14: warning: invalid conversion from 'const char*' to 'char' [-fpermissive]

   *ErrorC[1] =  "test2"; // assign the second value to a pointer 1

              ^

C:\Users\sterretje\AppData\Local\Temp\arduino_modified_sketch_519183\sketch_dec28a.ino:12:14: warning: invalid conversion from 'const char*' to 'char' [-fpermissive]

   *ErrorC[1] =  "test3"; // assign a new value to a pointer 1

              ^

C:\Users\sterretje\AppData\Local\Temp\arduino_modified_sketch_519183\sketch_dec28a.ino:4:12: warning: 'ErrorC' is used uninitialized in this function [-Wuninitialized]

   *ErrorC[0] =  "test1"; // assign the first value to a pointer 0

            ^

I can't help you further as I don't have time to dig too deep. There are people with a far deeper knowledge on this than me (see e.g. above).

@J-M-L

Thanks for the reply.
So am I correct, that rearrangement and new assignment would be done like following?

arrayOfConstCharPointers[0] = "Entry #4";  // new assignment of pointer
  arrayOfConstCharPointers[1] = arrayOfConstCharPointers[2]; // pointer rearangement 

  Serial.println(arrayOfConstCharPointers[0]);
  Serial.println(arrayOfConstCharPointers[1]);

And if so, why isn't the following correct?

 &(arrayOfConstCharPointers[1]) = &(arrayOfConstCharPointers[2]); // pointer rearangement 
//and
*arrayOfConstCharPointers[0] = "Entry #4";  // new assignment of pointer

As I thought assigning addresses would be done with "&" and values with "*".

pajavoe:
I am trying to use a very efficient way of rearranging large char arrays, without actually copying those arrays. Therefore my approach was to use pointers.

A clear description of the end goal may help others provide more direct answers.

@sterretje
Thanks for the reply. That is really a good suggestion! Thanks!
@dougp
Alright thanks!

The first assumptions is correct

For the second part, when doing an assignment you just reference the memory location. Say you have

int a[2];

if you want to store 5 in the bytes reserved for the first entry of the array, you just do

a[0]= 5;

. Same goes here: if you want to assign a pointer to the first entry of the array you do

arrayOfConstCharPointers[0] = "Entry #4";

If you want to use & or , use them in the right way and mind priority (precedence) of operators . You’ll see that Subscript [] is above indirection/dereference () or address of (&). For example *ptr++ is parsed as *(ptr++) and not (*ptr)++ which leads to something very different.

In your case how do you think *myArray[1] is parsed? Is it (*myArray)[1] or *(myArray[1])

@ J-M-L
Thank you very much, that helped a lot.
Just for clarification in that example :

arrayOfConstCharPointers[0] = "Entry #4";

So just the address of the char array ("Entry #4") is stored in the array of the pointers (arrayOfConstCharPointers). Where is the actual char array stored? And to what length is limited?

*myArray[1] is probably parsed as *(myArray[1]) isn't it?

Hi when the compiler sees a string literal such as "Entry #4" it stores the letters somewhere in memory (depends on the compiler where, could be in a non modifiable place - on your arduino it will end up in SRAM) and remembers the address where that was stored (so the pointer to the first character). When you do arrayOfConstCharPointers[0] = "Entry #4";what ends up in the first cell of the array is that pointer.

Read about string literal as per the link above to see what you can do with those. Such a cString is terminated by a null character and so by knowing where the string starts, you can manage the data by reading memory until you find that null byte.

On a small arduino like a UNO you will obviously be limited by the available SRAM and so it’s good to envision storing those string you don’t need to modify in flash memory instead (read about PROGMEM)

(Well done on the test :slight_smile: )

Thank you very much, very helpful ! :smiley:

One more question, that just appeared to me( or a new thread? :confused:)
When I just want to initialise an array of pointers, why do I have to give the storage size, although it seems not to matter what I put in there?

 const char * arrayOfConstCharPointers[1];

I am still able to call and assign pointers that are at position 2,3,4... of the array.

you need memory to store your pointers. if you want to keep 100 of them, you need to allocate enough space to store 100 pointers. Arrays in c++ do not grow on demand. they are fixed size.

I am still able to call and assign pointers that are at position 2,3,4... of the array.

Yes that's the beauty of C or C++ you can do stupid things... :slight_smile:
that will overwrite some other variable memory and trash your program. Sometimes the compiler will give you a warning but it can't catch them all.

Alright! Thanks. :smiley: