2

When a String variable is declared and modified inside a function, where does it go? To the heap(as string declaration is a part of dynamic memory allocation) or to the stack(as it is a part of function)? And is the space regained after the function goes out of the scope? For example

void loop()
{
  Serial.println(foo("def"));
  while(1);
}

String foo(String arg1)
{
 String test = "abc";
 test = test + arg1;
 return test;
}

So, in this case, the variable "test" is declared in stack or heap? And this space is regained at the end or not?

Modified: What if inside loop variable is declared like below?

String foo_test = F("def");
Serial.println(foo(foo_test));

In this case what will be the status of variables?

  • There is the String object, and then there is the character data referenced by that object. They don't live together. – Edgar Bonet Mar 14 '17 at 12:07
  • It compiled fine and showed the output too. What's wrong with that? – goddland_16 Mar 14 '17 at 12:10
  • @EdgarBonet you mean to say that string object will reside in stack and the char data reference will stay in heap? Pardon for misunderstanding in previous comment. – goddland_16 Mar 14 '17 at 13:06
  • The string object may reside in the function's stack, in the caller's stack, or in CPU registers. That's a matter of compiler optimization. For a simplified mental picture, you may assume it's in the function's stack. Everything should work “as if” that's the case. – Edgar Bonet Mar 14 '17 at 17:24
  • @EdgarBonet @Mikael@Michel Please see the modified – goddland_16 Mar 15 '17 at 7:21
3

This is an excellent question to illustrate the amount of string copying and heap operations (malloc/free) going on when using the Arduino String class.

void loop()
{
  Serial.println(foo("def"));
  while(1);
}

The compiler will generate loop() something like this:

void loop()
{
  // String literal is stored in program memory. Needs to be copied
  // to temporary variable. This is done before main() is called.
  static const char temp0[4] PROGMEM = "def";
  static const char temp1[4];
  copy_from_program_memory(temp1, temp0);

  // The parameter to the call to foo() is actually a temporary String 
  // variable constructed from the string literal. Storage is allocated 
  // on the heap and the assigned from the value of the string literal.
  String temp2;
  temp2.constructor(temp1);

  // The return value of the call to foo() is a String that is passed
  // to Serial.println(). This is also a temporary String variable. 
  String temp3;
  temp3.constructor(foo(temp2));
  Serial.println(temp3);

  // The String class destructor has to be called for the temporary
  // String variable so that the string values on heap are deallocated.
  temp2.destructor();
  temp3.destructor();
}

Note that each call to the constructor involves allocating and copying data to the heap.

String foo(String arg1)
{
 String test = "abc";
 test = test + arg1;
 return test;
}

The function foo() needs to copy strings several times to perform the string concatenation. The operator+ will require an intermediate String copy.

Cheers!

PS: For more details please see the assembly listing below with the calls to String member functions. The compiler reduces inline member functions and reuses temporary local variables. Also the call to foo() is inlined. The member function reserve() is part of the constructor.

00000198 <main>:
 1de:   60 df           rcall   .-320       ; 0xa0 <String::reserve(unsigned int)>
 1fc:   50 d2           rcall   .+1184      ; 0x69e <strcpy>
 212:   46 df           rcall   .-372       ; 0xa0 <String::reserve(unsigned int)>
 230:   36 d2           rcall   .+1132      ; 0x69e <strcpy>
 248:   62 df           rcall   .-316       ; 0x10e <String::operator=(String const&)>
 26a:   1a df           rcall   .-460       ; 0xa0 <String::reserve(unsigned int)>
 27e:   0f d2           rcall   .+1054      ; 0x69e <strcpy>
 28e:   3f df           rcall   .-386       ; 0x10e <String::operator=(String const&)>
 294:   60 df           rcall   .-320       ; 0x156 <String::~String()>
 29a:   5d df           rcall   .-326       ; 0x156 <String::~String()>
 2a0:   5a df           rcall   .-332       ; 0x156 <String::~String()>

Bottom-line is that the String class uses a lot of instruction cycles and memory, and there is a potential risk of heap fragmentation and allocation failure.

1

The string is an instance of an object, high likely keeping the administration like the offset and size (unless it is implemented as a 0-delimited string). This is a local variable and will be stored on the stack and will be nicely removed after finishing the function.

The characters itself ("abc" ...) will be stored on the heap since this needs to be dynamic. This data will be freed by the string object after returning from the function and possibly when needed to relocate the string.

Normally a string instance will keep some memory allocated, when it is increased (or increased too much) the string content will be copied. This all happens in heap and 'holes' in the heap can occur by this.

That's why in a memory/time critical process it's best to allocate the string before hand, of course this is only possible if you know the (maximum) string size.

  • You said this is a local variable and will be stored in stack. Did you mean to say variable "test" will be stored in stack?But when string manipulation occurs i.e test += arg1; isn't it that the size of test will increase in heap or is it stack? – goddland_16 Mar 14 '17 at 12:43
  • As Edgar already wrote, there are two items: the instance (keeping the administration), this is probably the pointer and possible the size of the string; this will be kept in stack. The memory allocation will never change. However, the characters itself are stored on the heap since this will change. When you add a character, depending on the implementation, possibly the whole string will be copied from one heap space to another location in the heap (using one more byte), and the pointer (stored in the stack) is pointing to the new allocated space in the heap. – Michel Keijzers Mar 14 '17 at 12:58
  • Also you can use the F(" ..." ) way, this puts the fixed strings in flash memory (of course for altering strings this is not possible). – Michel Keijzers Mar 14 '17 at 13:00
  • Yes, Explanation is good. – goddland_16 Mar 15 '17 at 7:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.