Array of Strings

Hello everyone..

I hade a look at the last example on this page:
https://www.arduino.cc/en/Reference/String

The code is:
char* myStrings[]={"This is string 1", "This is string 2", "This is string 3",
"This is string 4", "This is string 5","This is string 6"};

void setup(){
Serial.begin(9600);
}

void loop(){
for (int i = 0; i < 6; i++){
Serial.println(myStrings*);*

  • delay(500);*
  • }*
    }
    In the code they are making a array with String but what if I would like to add an extra String to the array later in the code. I use an API to get some information and want to sum it up in an array.
    So, how can I make an array and adding Strings to it?
    Regards
    MAttias
1 Like

The way that page is written is rather unfortunate. It uses the word String (capital S) which in C/C++ programming usually refers to the String class. I mention this because the distinction often arises in Forum Threads.

The reference page is actually using cstrings which are char arrays terminated with 0. This is appropriate for an Arduino because the String class can cause memory corruption in the small memory of the Arduino.

However, you cannot add elements to an array of cstrings when the program is running. You must define the space for all the elements you will need when you write the program.

If you were writing a program for a PC (with gigabytes of ram) you could use the String class and then you could add elements dynamically.

...R

1 Like

Robin2:
However, you cannot add elements to an array of cstrings when the program is running. You must define the space for all the elements you will need when you write the program.
...R

Oh, come now.... You know that's not really true. You can define the array to have more elements than are initialized, and use malloc() to allocate space at run-time for the strings to be added. It requires care, but can most certainly be done without gigabytes of RAM.

Regards,
Ray L.

@ray

Robin is right; if you have an array of cstrings defined at compile time you cannot add new strings elements to it.

To be specific - If you define at compile time an int arrayXYZ[20]; whatever you do with Malloc, you won't be able to extend that array to have 25 spaces.

You are right that you can define at run time an array of 25 spaces though, where 25 could be dynamically calculated.

1 Like

RayLivingston:
Oh, come now.... You know that's not really true. You can define the array to have more elements than are initialized, and use malloc() to allocate space at run-time for the strings to be added. It requires care, but can most certainly be done without gigabytes of RAM.

Regards,
Ray L.

Isnt that sorta what Robin2 is saying? You cant 'dynamically' add/adjust the length of the array? It has to be set at run time.

I think your focusing on the population aspect, which still may be a solution for the OP. (its not clear)

However I think Robin2's answer is legit.

1 Like

As mentioned, you can't enlarge a char array on the fly.
Also, as others have said, it's almost always far better to create your array in advance, with as many elements as you're likely to need.

But if you really needed to 'grow' the array dynamically, you can use new, (with 'delete' to clean up afterwards), to dynamically allocate a new larger array, then copy the original elements to the new array and add an extra string.

// Constants:-
const byte numChars = 10;
const char origArray[][numChars] = {"one", "two", "three", "four"};

// Global variables:-
int size = sizeof(origArray) / sizeof(origArray[0]);    // Record the number of strings in the array

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

    // Print the original array:-
    for (int i = 0; i < size; i++)
        Serial.println(origArray[i]);
    Serial.println();

    // Create the new 2D array:-
    byte numStrings = size + 1;                 // Make the new array one element longer than the original.
    char **newArray = new char*[numStrings];
    for ( int i = 0; i < numStrings; i++ )
        newArray[i] = new char[numChars];

    // Copy the old array to the new one:-
    for (int i = 0; i < size; i++)
        strcpy(newArray[i], origArray[i]);

    // Add the new string:-
    strcpy(newArray[size], "five");
    size = numStrings;                          // Record the new size

    // Print the new 2D array:-
    for (int i = 0; i < size; i++)
        Serial.println(newArray[i]);

    // Delete the new 2D array:-
    for ( int i = 0; i < size; i++ )
        delete[] newArray[i];
    delete[] newArray;
}

void loop() {}

Remember, this is just a technical exercise. It's almost as evil as using the "String" class. :smiling_imp:

1 Like

RayLivingston:
use malloc() to allocate space at run-time for the strings to be added

I am happy for you to write the beginners tutorial for malloc() and its inverse and how not to corrupt your 2k of SRAM.

I think my advice is reasonable for a beginner, and for many others too.

...R

"Corrupt" might be a bit extreme: using malloc and free does not corrupt memory - the functions behave the right way.

The challenge is that by doing so repetitively in diverse size patters, you are likely shooting holes into the heap and making that memory space like French gruyere cheese (really good to eat)


and after a while you cannot get a slice of cheese that has no hole in it ... and so when you ask the system for a bloc without holes it tells you "sorry mate, can't do that anymore, here is a NULL pointer" - and because most programs don't even check for that answer to try to do something agressive with cleaning up heap memory, then the program tries to save stuff at the NULL address and fails miserably....

More elaborate operating systems or run time environment would run a garbage collector and move data around memory so that all the holes go back together and you get nice blocs of Swiss gruyere (the one without the holes - really good too :)) ) to work from. But that's not what an Arduino / C++ typical bare bone environment do.

J-M-L:
"Corrupt" might be a bit extreme: using malloc and free does not corrupt memory - the functions behave the right way.

I know that. But it seems to be very easy to use them the wrong way. Many "expensive" programs seem to suffer from memory leaks.

...R

OldSteve:
. . .
But if you really needed to 'grow' the array dynamically, you can use new, (with 'delete' to clean up afterwards), to dynamically allocate a new larger array, then copy the original elements to the new array and add an extra string.
. . .

Some programming languages really implement an array append operation exactly like that. For example, in Windows Powershell the innocent looking:

$myArray += $myNextElement

completely rebuilds the array to include the new element. This gives rise to the situation that programs created and tested with small volumes of data work perfectly but fail with production volumes of data because, as the size of the array increases, the time of the array rebuild operation increases exponentially.

OldSteve:
. . .
It's almost as evil as using the "String" class. :smiling_imp:

I think we'd better start getting used to the String class. It is used extensively in the examples for the ESP8266 web functions. Dynamically building an HTML page using the native C/C++ character arrays is a misery compared with using Strings

J-M-L:
The challenge is that by doing so repetitively in diverse size patters, you are likely shooting holes into the heap and making that memory space like French gruyere cheese (really good to eat)

Just to ensure that some important facts are not misrepresented:

  1. Gruyere cheese is Swiss, not French.
  2. The cheese in the picture looks like Emmental , another Swiss cheese, famous for its holes.
    :slight_smile:
1 Like

6v6gt:
Just to ensure that some important facts are not misrepresented:

  1. Gruyere cheese is Swiss, not French.
  2. The cheese in the picture looks like Emmental , another Swiss cheese, famous for its holes.
    :slight_smile:

Wait wait wait - that's more complicated than this! :slight_smile:

Gruyere is indeed a region in switzerland (canton of Fribourg) but French cheesemakers have been allowed to share the name Gruyère with the Swiss under a number of conditions after a big discussion at EU level: the Farming commission ­officials ruled French Gruyère must have holes to ­distinguish it from Switzerland’s famous solid, centuries-old variety. The EU said: “ French Gruyère must contain holes between the size of a pea and a cherry".

Emmental also takes roots in Switzerland (canton of Berne) but has bigger holes

signed: a cheese lover :slight_smile:

(don't want to start a war on cheese though)

J-M-L:
(don't want to start a war on cheese though)

That's simple. The next time you use a holey cheese to illustrate a fragmented heap, choose the genuine article and then there is no discussion !

Thanks for all of your replies! I now know some more of "array of strings" and alot more regarding cheese. =)

Let's say that I know the size of the array and just want to add and replace strings in a predefined array.
Is that possible? How do I do ? I tried something like this without success:

char* myStrings[15]= {"This is string 1", "This is string 2", "This is string 3", "This is string 4", "This is string 5","This is string 6"};

myStrings[1] = "This is still string 1";

void setup(){
Serial.begin(115200);
}

void loop(){
for (int i = 0; i < 6; i++){
Serial.println(myStrings*);*

  • delay(500);*
  • }*
    }

6v6gt:
I think we'd better start getting used to the String class. It is used extensively in the examples for the ESP8266 web functions. Dynamically building an HTML page using the native C/C++ character arrays is a misery compared with using Strings

There are definitely times when the String class is useful, but the problem is that beginners use it for absolutely everything, in preference to learning how to work with C strings. The result is unreliability.

6v6gt:
That's simple. The next time you use a holey cheese to illustrate a fragmented heap, choose the genuine article and then there is no discussion !

yes but I wanted lots of small holes

Swiss gruyère does not have any and Emmental has bigger holes

:slight_smile:

1 Like

mdahlb:
Thanks for all of your replies! I now know some more of "array of strings" and alot more regarding cheese. =)

Let's say that I know the size of the array and just want to add and replace strings in a predefined array.
Is that possible? How do I do ? I tried something like this without success:

please post code with code tags otherwise your i index in brackets disappear and are interpreted as a command to put text into italics...

have a look at this

const char* myStrings[15] = {"This is string 1", "This is string 2", "This is string 3", "This is string 4", "This is string 5", "This is string 6"};


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

  for (int i = 0; i < 6; i++) {
    Serial.print("myStrings["); Serial.print(i); Serial.print("] = "); Serial.println(myStrings[i]);
  }

  myStrings[1] = "This is string [1] MODIFIED";

  Serial.println("------------ now it's modififed ------------ ");

  for (int i = 0; i < 6; i++) {
    Serial.print("myStrings["); Serial.print(i); Serial.print("] = "); Serial.println(myStrings[i]);
  }
}

void loop() {
}

Thanks for your help!

So much fear of things that have been common practice in embedded systems for decades.... I started using malloc() in the '70s, when that was all we had. Back then, even 2K of RAM was all but unheard of in most embedded systems.

Contrary to the impression one gets reading this thread, malloc() does NOT automatically lead to fragmentation, and can be safely used even on small systems with a little care. Sure, if you constantly, randomly allocate and de-allocate memory you are likely to get into trouble at some point. But that's true no matter how much memory you have. And, if the code is poorly written, and does not check the result of each malloc(), and ensure each malloc() has a matching free(), then you will also get into trouble, no matter how much memory you have.

However, a large range of applications can be designed to do huge numbers of malloc()'s in perfect safety. One of my current projects does literally thousands of malloc() calls every single second it's running, and will run essentially indefinitely with zero "leakage". In many, many cases, the usage amounts to malloc(), malloc(), malloc(), etc. followed by free(), free(), free(). At the end of all the frees, the heap is back to a single, large, contiguous block, exactly as it was on startup. As long as peak usage does not exceed available memory, you can repeat this sequence indefiniteiy. There are many, many well-known methods of avoiding fragmentation in cases where more chaotic patterns are necessary.

Sure, malloc() requires some care, and is likely beyond the safe reach of a "newbie", but don't tell them it "can't" be done. Instead, tell them it can be done, but requires an experienced programmer to do it safely.

Regards,
Ray L.