I get an unpredicted behaviour of a program while trying to fill a dynamic array of String objects. I've tried to simplify the program, but couldn't figure out the cause.
FYI: debug() and debugLn() print data to Serial, debugMem() prints free memory amount.
So, the simplest version works as expected (the scenario - allocate a global array of 3 Strings, define 3 local variables and assign their values to array elements):
String* arr;
void setup() {
Serial.begin(9600);
debugMem("1");
String src = "abcdefghijk";
arr = malloc(3 * sizeof(String));
debugMem("2");
String dest1 = src;
String dest2 = src;
arr[0] = src.substring(0);
arr[1] = dest1.substring(0);
arr[2] = dest2.substring(0);
debugMem("3");
dest1.setCharAt(1, 'z');
dest2.setCharAt(1, 'x');
debug(F("src: "));
debugln(src);
debug(F("src located at "));
debugln((long)&src);
debug(F("dest1: "));
debugln(dest1);
debug(F("dest1 located at "));
debugln((long)&dest1);
debug(F("dest2: "));
debugln(dest2);
debug(F("dest2 located at "));
debugln((long)&dest2);
debug(F("arr[0]: "));
debugln(arr[0]);
debug(F("arr[0] located at "));
debugln((long)(arr + 0));
debug(F("arr[1]: "));
debugln(arr[1]);
debug(F("arr[1] located at "));
debugln((long)(arr + 1));
debug(F("arr[2]: "));
debugln(arr[2]);
debug(F("arr[2] located at "));
debugln((long)(arr + 2));
debugMem("4");
}
Its output:
>>>>>>>>>>1787<<<<<<<<<<1
>>>>>>>>>>1753<<<<<<<<<<2
>>>>>>>>>>1683<<<<<<<<<<3
src: abcdefghijk
src located at 2294
dest1: azcdefghijk
dest1 located at 2288
dest2: axcdefghijk
dest2 located at 2282
arr[0]: abcdefghijk
arr[0] located at 493
arr[1]: abcdefghijk
arr[1] located at 499
arr[2]: abcdefghijk
arr[2] located at 505
>>>>>>>>>>1683<<<<<<<<<<4
(Though I'm a bit confused with the addresses of src, dest1, dest2 that is greater then the total amount of 2048 bytes of RAM).
The second version does actually the same (pushing values from local var to global array), but its behaviour is "completely different" (the scenario - init a dummy source string, parse it in a function, reallocating a global array to store parsed strings, assign a value from local variable to every array element):
String* phones;
int phonesCount;
void setup() {
Serial.begin(9600);
String reply = "1234567890,2345678901,3456789012";
fillPhones(reply);
}
void loop() {
debugln(F("loop"));
for(int i = 0; i < phonesCount; i++) {
debug(F("Phone"));
debugln(i);
debug(F("Phone located at "));
debugln((long)(phones + i));
debug(F("Phone: "));
debugln(*(phones + i));
}
}
void fillPhones(String reply) {
phonesCount = parsePhones(reply, phones);
}
int parsePhones(String reply, String* &phonesArray) {
debugMem("1");
phonesArray = NULL;
int count = 0;
int fromIndex = 0;
int toIndex = 0;
while(toIndex >= 0) {
debug(F("Phone index: "));
debugln(count);
toIndex = reply.indexOf(F(","), fromIndex);
debug(F("Substring: "));
debug(fromIndex);
debug(F(".."));
debugln(toIndex);
debugMem("2");
String entryPhone = reply.substring(fromIndex, toIndex);
debugMem("3");
debug(F("Phone to add: "));
debugln(entryPhone);
debug(F("Phone located at "));
debugln((long)&entryPhone);
String* tmpArray = realloc(phonesArray, (count + 1) * sizeof(String));
if(tmpArray == NULL) {
break;
}
debug(F("Array allocated bytes: "));
debugln((count + 1) * sizeof(String));
debug(F("Array located at "));
debugln((long)tmpArray);
debugMem("4");
phonesArray = tmpArray;
debugMem("5");
phonesArray[count] = entryPhone;
debug(F("Added phone: "));
debugln(phonesArray[count]);
debug(F("Added phone located at "));
debugln((long)(phonesArray + count));
debugMem("6");
count++;
fromIndex = toIndex + 1;
}
debug(F("Total phones count:"));
debugln(count);
debugMem("7");
return count;
}
Its output:
>>>>>>>>>>1647<<<<<<<<<<1
Phone index: 0
Substring: 0..10
>>>>>>>>>>1647<<<<<<<<<<2
>>>>>>>>>>1634<<<<<<<<<<3
Phone to add: 1234567890
Phone located at 2273
Array allocated bytes: 6
Array located at 623
>>>>>>>>>>1626<<<<<<<<<<4
>>>>>>>>>>1626<<<<<<<<<<5
Added phone: 1234567890
Added phone located at 623
>>>>>>>>>>1613<<<<<<<<<<6
Phone index: 1
Substring: 11..21
>>>>>>>>>>1626<<<<<<<<<<2
>>>>>>>>>>1613<<<<<<<<<<3
Phone to add: 2345678901
Phone located at 2273
Array allocated bytes: 12
Array located at 657
>>>>>>>>>>1607<<<<<<<<<<4
>>>>>>>>>>1607<<<<<<<<<<5
Added phone: 2345678901
Added phone located at 663
>>>>>>>>>>1607<<<<<<<<<<6
Phone index: 2
Substring: 22..-1
>>>>>>>>>>1620<<<<<<<<<<2
>>>>>>>>>>1607<<<<<<<<<<3
Phone to add: 3456789012
Phone located at 2273
Array allocated bytes: 18
Array located at 657
>>>>>>>>>>1601<<<<<<<<<<4
>>>>>>>>>>1601<<<<<<<<<<5
Added phone: 3456789012
Added phone located at 669
>>>>>>>>>>1601<<<<<<<<<<6
Total phones count:3
>>>>>>>>>>1614<<<<<<<<<<7
loop
Phone0
Phone located at 657
Phone: 1234567890
Phone1
Phone located at 663
Phone: ‚56789012
Phone2
Phone located at 669
Phone: 3456789012
As you can see, while there are no observable fails on filling, it outputs shit in a loop.
I wish to know what the right way to add a String to a (dynamic) array of Strings is. Why does the first version work fine? Is it just because it's so small/simple?
PS1: short version of the first example (w/o debug output):
String* arr;
void setup() {
Serial.begin(9600);
String src = "abcdefghijk";
arr = malloc(3 * sizeof(String));
String dest1 = src;
String dest2 = src;
arr[0] = src.substring(0);
arr[1] = dest1.substring(0);
arr[2] = dest2.substring(0);
dest1.setCharAt(1, 'z');
dest2.setCharAt(1, 'x');
}
PS2: short version of the second example (with min debug output):
String* phones;
int phonesCount;
void setup() {
Serial.begin(9600);
String reply = "1234567890,2345678901,3456789012";
fillPhones(reply);
}
void loop() {
debugln(F("loop"));
for(int i = 0; i < phonesCount; i++) {
debug(F("Phone"));
debugln(i);
debug(F("Phone located at "));
debugln((long)(phones + i));
debug(F("Phone: "));
debugln(*(phones + i));
}
}
void fillPhones(String reply) {
phonesCount = parsePhones(reply, phones);
}
int parsePhones(String reply, String* &phonesArray) {
phonesArray = NULL;
int count = 0;
int fromIndex = 0;
int toIndex = 0;
while(toIndex >= 0) {
toIndex = reply.indexOf(F(","), fromIndex);
String entryPhone = reply.substring(fromIndex, toIndex);
String* tmpArray = realloc(phonesArray, (count + 1) * sizeof(String));
if(tmpArray == NULL) {
break;
}
phonesArray = tmpArray;
phonesArray[count] = entryPhone;
count++;
fromIndex = toIndex + 1;
}
return count;
}
mallocin a C++ program. 2) learn aboutstd::vector. 3) learn about smart-pointers. 4) if you insist on using (horrible) raw pointers; at least do it correctly and clean up after yourself - your program leaks.std::unique_ptroptimizes away completely - it's a 0-overhead abstraction. Besides; if that was all that was wrong with OPs code we'd be golden. But it's not. It's basically bad C made to compile with a C++ compiler.String? You haven't showed us its definition/code.stdon an Arduino. AndStringis a Arduino specific class.