Skip to main content
Arduino

Return to Answer

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

However, if the statement String s = f(); is replaced by String s; s = f();, then the string is copied when returned to the caller, which doubles the amount of RAM required to handle it. This is presumably because only the first form allows copy elision optimization copy elision optimization. (This paragraph was added after reading Nick Gammon's answer, which made me realize the copy elision issue).

However, if the statement String s = f(); is replaced by String s; s = f();, then the string is copied when returned to the caller, which doubles the amount of RAM required to handle it. This is presumably because only the first form allows copy elision optimization. (This paragraph was added after reading Nick Gammon's answer, which made me realize the copy elision issue).

However, if the statement String s = f(); is replaced by String s; s = f();, then the string is copied when returned to the caller, which doubles the amount of RAM required to handle it. This is presumably because only the first form allows copy elision optimization. (This paragraph was added after reading Nick Gammon's answer, which made me realize the copy elision issue).

Added check of buffer address after return + short paragraph on copy elision.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81
extern uintptr_t __brkval;
String f() {
 String s("");
 const char * p = s.c_str();
 Serial.print(F(" f(): string buffer @ 0x"));
 Serial.println((uintptr_t) p, 16);
 for (int i = 0; i < random(8) + 1; i++)
 s += random(2) ? "a": "bc";
 if (p != s.c_str())
 Serial.println(F("Warning: memory fragmentation."));
 Serial.print(F(" top of heap at 0x"));
 Serial.println(__brkval, 16);
 return s;
}
void setup() {
 Serial.begin(9600);
 Serial.print(F("*** heap starts at 0x"));
 Serial.println((uintptr_t) __malloc_heap_start, 16);
}
void loop() {
 Serial.print(F("loop(): top of heap at 0x"));
 Serial.println(__brkval, 16);
 String s = f();
 Serial.print(F(" f() returned "));
 Serial.println(s);
 Serial.print(F(" with buffer at 0x"));
 Serial.println((uintptr_t) s.c_str(), 16);
 delay(20001000);
}
*** heap starts at 0x1D7
loop(): top of heap at 0x0
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DC
 f() returned aa
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1E2
 f() returned aabcabca
 with buffer at 0x1D9
[...]
  • the heap is always empty (top = bottom) at the beginning of loop()
  • the string is always allocated at the same place
  • the heap size inside f() varies depending on the string's length
  • the string is not copied when returned to the caller, as the internal buffer is still at the same address.

However, if the statement String s = f(); is replaced by String s; s = f();, then the string is copied when returned to the caller, which doubles the amount of RAM required to handle it. This is presumably because only the first form allows copy elision optimization . (This paragraph was added after reading Nick Gammon's answer, which made me realize the copy elision issue).

extern uintptr_t __brkval;
String f() {
 String s("");
 const char * p = s.c_str();
 Serial.print(F(" f(): string buffer @ 0x"));
 Serial.println((uintptr_t) p, 16);
 for (int i = 0; i < random(8) + 1; i++)
 s += random(2) ? "a": "bc";
 if (p != s.c_str())
 Serial.println(F("Warning: memory fragmentation."));
 Serial.print(F(" top of heap at 0x"));
 Serial.println(__brkval, 16);
 return s;
}
void setup() {
 Serial.begin(9600);
 Serial.print(F("*** heap starts at 0x"));
 Serial.println((uintptr_t) __malloc_heap_start, 16);
}
void loop() {
 Serial.print(F("loop(): top of heap at 0x"));
 Serial.println(__brkval, 16);
 String s = f();
 Serial.print(F(" f() returned "));
 Serial.println(s);
 delay(2000);
}
*** heap starts at 0x1D7
loop(): top of heap at 0x0
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DC
 f() returned aa
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1E2
 f() returned aabcabca
[...]
  • the heap is always empty (top = bottom) at the beginning of loop()
  • the string is always allocated at the same place
  • the heap size inside f() varies depending on the string's length.
extern uintptr_t __brkval;
String f() {
 String s("");
 const char * p = s.c_str();
 Serial.print(F(" f(): string buffer @ 0x"));
 Serial.println((uintptr_t) p, 16);
 for (int i = 0; i < random(8) + 1; i++)
 s += random(2) ? "a": "bc";
 if (p != s.c_str())
 Serial.println(F("Warning: memory fragmentation."));
 Serial.print(F(" top of heap at 0x"));
 Serial.println(__brkval, 16);
 return s;
}
void setup() {
 Serial.begin(9600);
 Serial.print(F("*** heap starts at 0x"));
 Serial.println((uintptr_t) __malloc_heap_start, 16);
}
void loop() {
 Serial.print(F("loop(): top of heap at 0x"));
 Serial.println(__brkval, 16);
 String s = f();
 Serial.print(F(" f() returned "));
 Serial.println(s);
 Serial.print(F(" with buffer at 0x"));
 Serial.println((uintptr_t) s.c_str(), 16);
 delay(1000);
}
*** heap starts at 0x1D7
loop(): top of heap at 0x0
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DC
 f() returned aa
 with buffer at 0x1D9
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1E2
 f() returned aabcabca
 with buffer at 0x1D9
[...]
  • the heap is always empty (top = bottom) at the beginning of loop()
  • the string is always allocated at the same place
  • the heap size inside f() varies depending on the string's length
  • the string is not copied when returned to the caller, as the internal buffer is still at the same address.

However, if the statement String s = f(); is replaced by String s; s = f();, then the string is copied when returned to the caller, which doubles the amount of RAM required to handle it. This is presumably because only the first form allows copy elision optimization . (This paragraph was added after reading Nick Gammon's answer, which made me realize the copy elision issue).

+ test program.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

Edit: I wrote this small test program to show the behaviour of the heap when you use string concatenation this way:

extern uintptr_t __brkval;
String f() {
 String s("");
 const char * p = s.c_str();
 Serial.print(F(" f(): string buffer @ 0x"));
 Serial.println((uintptr_t) p, 16);
 for (int i = 0; i < random(8) + 1; i++)
 s += random(2) ? "a": "bc";
 if (p != s.c_str())
 Serial.println(F("Warning: memory fragmentation."));
 Serial.print(F(" top of heap at 0x"));
 Serial.println(__brkval, 16);
 return s;
}
void setup() {
 Serial.begin(9600);
 Serial.print(F("*** heap starts at 0x"));
 Serial.println((uintptr_t) __malloc_heap_start, 16);
}
void loop() {
 Serial.print(F("loop(): top of heap at 0x"));
 Serial.println(__brkval, 16);
 String s = f();
 Serial.print(F(" f() returned "));
 Serial.println(s);
 delay(2000);
}

The output of the program is:

*** heap starts at 0x1D7
loop(): top of heap at 0x0
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DC
 f() returned aa
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1E2
 f() returned aabcabca
[...]

The very first "top of heap at" is bogus because at this point the malloc library has not been initialized. Besides that, you can see here that:

  • the heap is always empty (top = bottom) at the beginning of loop()
  • the string is always allocated at the same place
  • the heap size inside f() varies depending on the string's length.

Keep in mind that this works so nicely only because there is nothing else doing dynamic allocation in this small program. This usage pattern is safe, but once you start doing dynamic allocation in different parts of the program, it becomes difficult to ensure that the allocation/deallocation pattern is still safe vs. memory fragmentation.


Edit: I wrote this small test program to show the behaviour of the heap when you use string concatenation this way:

extern uintptr_t __brkval;
String f() {
 String s("");
 const char * p = s.c_str();
 Serial.print(F(" f(): string buffer @ 0x"));
 Serial.println((uintptr_t) p, 16);
 for (int i = 0; i < random(8) + 1; i++)
 s += random(2) ? "a": "bc";
 if (p != s.c_str())
 Serial.println(F("Warning: memory fragmentation."));
 Serial.print(F(" top of heap at 0x"));
 Serial.println(__brkval, 16);
 return s;
}
void setup() {
 Serial.begin(9600);
 Serial.print(F("*** heap starts at 0x"));
 Serial.println((uintptr_t) __malloc_heap_start, 16);
}
void loop() {
 Serial.print(F("loop(): top of heap at 0x"));
 Serial.println(__brkval, 16);
 String s = f();
 Serial.print(F(" f() returned "));
 Serial.println(s);
 delay(2000);
}

The output of the program is:

*** heap starts at 0x1D7
loop(): top of heap at 0x0
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DF
 f() returned abcbc
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1DC
 f() returned aa
loop(): top of heap at 0x1D7
 f(): string buffer @ 0x1D9
 top of heap at 0x1E2
 f() returned aabcabca
[...]

The very first "top of heap at" is bogus because at this point the malloc library has not been initialized. Besides that, you can see here that:

  • the heap is always empty (top = bottom) at the beginning of loop()
  • the string is always allocated at the same place
  • the heap size inside f() varies depending on the string's length.

Keep in mind that this works so nicely only because there is nothing else doing dynamic allocation in this small program. This usage pattern is safe, but once you start doing dynamic allocation in different parts of the program, it becomes difficult to ensure that the allocation/deallocation pattern is still safe vs. memory fragmentation.

Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /