0

To the best of my understanding, when I declare

String s = "This is a string.";

what happens is that space is allocated on the stack for a pointer which points to some String object which internally contains a char array. My confusion stems from the fact that this pointer seems(I could be wrong) to behave differently than any other type of pointer. For example, if I write

int a = 1;
int* x = &a;
int* y = x;

what happens is that the pointer saved in y now points to the same location in memory as the pointer saved at x. So if I write

String s1 = "This is a string.";
String s2 = "Hello World!";
s1 = s2;

does the pointer saved at s1 now point to the same memory location as s2? Or does s1 copy the char array from s2 into its own separate memory? Further, If I write

String s1 = "This is a string.";
s1 = "Hello World!";

does s1 now point to whatever arbitrary memory location was created for the string literal "Hello World!"? Will this memory location be freed later, or is some sort of smart garbage collector in place which knows that I am using that memory in the heap? Finally, should I never assign a string declared on the stack to a global variable, as this will be referencing a memory location which will be freed soon?

Thank you in advance for your help!

5

String is not a simple type like an int or a char. It is a class with many member functions and, more importantly, operators. When you create the object it allocates room for that object either on the stack (for a local variable) or in the global data area if it's a global variable. However that object doesn't contain the memory used to store the actual string data - it only has a pointer. The memory for the string data is allocated on the heap using malloc() and realloc().

Assigning string data to the String object is done through the = operator defined in the class, of which there are a number of overloaded varieties:

String & String::operator = (const String &rhs)
{
    if (this == &rhs) return *this;

    if (rhs.buffer) copy(rhs.buffer, rhs.len);
    else invalidate();

    return *this;
}

#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & String::operator = (String &&rval)
{
    if (this != &rval) move(rval);
    return *this;
}

String & String::operator = (StringSumHelper &&rval)
{
    if (this != &rval) move(rval);
    return *this;
}
#endif

String & String::operator = (const char *cstr)
{
    if (cstr) copy(cstr, strlen(cstr));
    else invalidate();

    return *this;
}

String & String::operator = (const __FlashStringHelper *pstr)
{
    if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
    else invalidate();

    return *this;
}

They all, ultimately, call the copy() or move() functions which copy the data from the source into the memory allocated on the heap (increasing the allocation if needed).

Using:

String s1 = "Foo";
String s2 = s1;

s1 has memory allocated on the heap, then Foo\0 copied into it. s2 then also has memory allocated on the heap, and the data from s1 is copied into it. So you have two separate objects with separate memory allocated for their content. So then doing:

s2 += "bar";

you end up with:

s1: "Foo\0"
s2: "Foobar\0"

Since it's an object that's allocated on the stack (or in global space) and not a pointer to the object you can get a pointer to it like with an int:

String s1 = "Foo";
String *s2 = &s1;
(*s2) += "bar";

Which gives you just one object and one memory space, but two names for it, and the result is:

s1: "Foobar\0"
s2: "Foobar\0"

This is why it's important to use either pointers or references when passing String objects as parameters to functions, and the constant allocation and reallocation on the heap why String objects are frowned upon in microcontrollers with very limited RAM.

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.