How do string pointers work in Arduino?
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 &amp; String::operator = (const String &amp;rhs)
{
    if (this == &amp;rhs) return *this;

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

    return *this;
}

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

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

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

    return *this;
}

String &amp; 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 = &amp;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.