I return a char[300]
array from a function. If I intialize a char*
variable with it, the return is garbled, but not if I append it to a String
. What gives?
const char* post2 = uploadHourCsv(timeNow,pulseChangeHour) ;
String postS2 = "";
postS2 = uploadHourCsv(timeNow,pulseChangeHour) ;
//const char* post2 = { uploadHourCsv(timeNow,pulseChangeHour) };
if( debug ) {
Serial.print("received in loop() as : [");
Serial.println(post2);
Serial.print("String : [");
Serial.println(postS2);
}
...
char postStr[300] = "";
// populate array
if( debug ) {
Serial.print("postStrCsv generated: ");
Serial.println(postStr);
}
return postStr;
postStrCsv generated: 00000003;3| 0.00;8|17.55;9|17.55;10|18.12;11|16.92;20|93817;22|93789;101|6;time|1559646000;
received in loop() as : [000?⸮@⸮⸮?" ⸮⸮?17.55;9|17.58! @0⸮⸮⸮⸮G% @5~`⸮h⸮?⸮⸮
String : [00000003;3| 0.00;8|17.55;9|17.55;10|18.12;11|16.92;20|93817;22|93789;101|6;time|1559646000;
2 Answers 2
It's because you're returning a pointer to a local variable.
You are only returning the address in memory where your char array is allocated. That allocation is on the stack and only exists for the lifetime of the function. But you still have the address where that memory was allocated, and you're using it as if it's still allocated.
When you append it to a String room is allocated on the heap and the data is copied in. That heap allocation remains until the String object is destroyed, either by going out of scope or by you manually destroying it.
It's pure chance that the data is still available and intact at the point of copying.
In short:
- You must never return a pointer to a locally allocated (non-static) array.
Things you can do that are correct:
- Pass an array pointer to the function to be populated by the function
- Return a pointer to a statically allocated local array
- Return a
String
object (though I wouldn't recommend using String for anything)
My preferred method is the first, whereby you define an array in the outer scope, then pass that (and possibly the array length) to the function. The function then populates the array it has been passed. This also has the advantage that the function can use the return value to indicate a status or other quantity.
For example:
void myFunc(char *buf, int len) {
for (int i = 0; i < len - 1; i++) {
buf[i] = 'A';
}
buf[len - 1] = 0;
}
char myBuf[20];
myFunc(myBuf, 20);
Serial.println(myBuf);
--> AAAAAAAAAAAAAAAAAAAA
-
1U da man! UPVOTED and ACCEPTED. Re use of
String
: totally agree, unsafe memory allocation in String class.tony gil– tony gil2019年06月04日 14:50:31 +00:00Commented Jun 4, 2019 at 14:50
Following Majenko's invaluable advice and incorporating another similar approach, final working implementation was to treat the function as a method to operate upon the char[300]
array as "out parameter".
char postStr[300] = "";
uploadHourCsv(timeNow,pulseChangeHour,postStr) ;
...
void uploadHourCsv(int unixtimeEvent, int pulseChange, char* postStr) {
// prepare data and variables
strcat(postStr, stationId);
strcat(postStr,";3|"); // chuva hora
strcat(postStr, dtostrf(rainHour,6,2,charDummy));
...
}
-
2If you make the first
strcat
astrcpy
instead, you can avoid initializing the array first. (And note that the way you initialize it in the caller fills the entire length with zero bytes, not just the first element, so that takes a lot longer than necessary unless it gets optimized away.)Peter Cordes– Peter Cordes2019年06月05日 10:29:54 +00:00Commented Jun 5, 2019 at 10:29
uploadHourCsv
return? Since post2 is declared as a pointer, without any size, the function has to return a pointer to an already allocated string to do this.char postStr[300]
with contents displayed in blockquote.