0

Let's say I've got this simple code:

class Foo
{
private:
    const char* str;

public:
    void Set(const char* c) { str = c; }
    const char* Get() const { return str; }
};

Foo f;

void func1()
{
    // Please note: Comments of the answer below refer to const char* localStr.
    // I posted the wrong code.
    const char localStr[] = "Hello world!";

    //f.Set(localStr);      // Of course, does not work, std::cout prints garbage
    f.Set("Hello world!");  // Works
}

int main()
{
    func1();

    std::cout << "Get: " << f.Get() << std::endl;
}

I'm trying to understand why exactly f.Set("Hello world!"); is working.

For my understanding f.Set(localStr); of course does not work. When func1() returns, localStr goes out of scope and Foo::str points to garbage.

But what is happening with f.Set("Hello world!");? I thought that the string argument is also a temporary/local const char[] which gets passed to Foo::Set as a pointer (to it's first element). Why is Foo::str still valid after the call when the passed argument is only a temporary/local?

Marc
  • 338
  • 2
  • 15
  • Don't do any of this. Use `std::string`. – n. m. could be an AI Sep 11 '21 at 08:01
  • @n.1.8e9-where's-my-sharem. Let me quote myself: `I'm trying to understand why [...]`. Exactly because I'm always using `std::string`, I had to ask this question in the first place. If one doesn't fully understand the underlaying basics one shouldn't use concepts building on these basics. – Marc Sep 11 '21 at 08:13
  • I didn't downvote this question but I think it lacks some research effort. There are already similar questions, e.g. [here](https://stackoverflow.com/questions/36020149/how-to-design-class-that-has-char-pointer-as-class-member-variable). BTW.: You joined SO in 2014, so I'm not considering you as a new user. – MarcusS Sep 11 '21 at 08:17
  • Understanding the lifetime of literals and local variables is pretty essential, regardless of `std::string` (which is why I said "don't use this thing" rather than "don't try to understand it"). However in general, in order yo use a library you don't have to understand its underlying mechanisms. That's kinda the point of libraries. – n. m. could be an AI Sep 11 '21 at 08:30
  • @MarcusS The whole point of my question was not to design such a class (like I said, I use std::string in 99% of all cases), but to understand why this behavior is like it is. Since I didn't know string literals are static, it's quiet hard to come up with keywords that lead me to an answer. BTW: I registered 7 years ago and did not visit the site once since then. If time of registration and not the activity (active or passive) is considered as experience on the site, then yes I am quite an experienced SO user... :) – Marc Sep 11 '21 at 08:30
  • @n.1.8e9-where's-my-sharem. Uhm, yes. It is essential. That's why I even came up with this experiment. This is not a code I found or am using in a project, it exists to _understand_ things. If you don't want to/need to understand the workings of systems/libs you're using, that's your choice. I don't like that. – Marc Sep 11 '21 at 08:36

1 Answers1

4

But what is happening with f.Set("Hello world!");?

String literals have static storage duration.

Storing char array as class member

To be clear, the member of your class is not an array. It is a pointer.

const char* localStr = "Hello world!";
//f.Set(localStr);      // Of course, does not work, std::cout prints garbage

This (of course) works as well and std::cout won't print garbage.

const char localStr[] = "Hello world!";
//f.Set(localStr);      // Of course, does not work, std::cout prints garbage

This array has automatic storage, so the pointer is invalidated when the function returns.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thanks that was the missing link to understand it. Second part: Yes that is clear to me. I could not think of a better title right now. However there are still so many devs saying arrays=pointer. – Marc Sep 11 '21 at 07:13
  • So wouldn't `localStr` contain a pointer to this storage with static storage duration? How does copying the address twice break things here? – fabian Sep 11 '21 at 07:14
  • @fabian Yes, `localStr` points to the string literal that has static storage duration. The pointer will remain valid when the function returns. – eerorika Sep 11 '21 at 07:16
  • @eerorika I have to ask then: If the `localStr` pointer stays valid after return, why is `Foo::str` not valid after return? `Foo::str` is pointing to the same address as `localStr`, doesn't it? – Marc Sep 11 '21 at 07:26
  • @Marc `Foo::str is pointing to the same address as localStr, doesn't it?` Yes. `If the localStr pointer stays valid after return, why is Foo::str not valid after return?` You assume wrongly. It is valid. – eerorika Sep 11 '21 at 07:31
  • @eerorika Now you confused me. If `Foo::str` stays valid when using `Set` with `localStr`, why can't I print the contents of `Foo::str` then? – Marc Sep 11 '21 at 07:35
  • @Marc You *can* print it. – eerorika Sep 11 '21 at 07:35
  • Well yes I can, but the content is garbage. If the pointer stays valid (pointing to the static str literal) and the string literal is static, why is the output garbage? – Marc Sep 11 '21 at 07:37
  • @Marc The content won't be garbage. – eerorika Sep 11 '21 at 07:38
  • Oh yes you're right, I messed up the code. Let me try again: If I use `const char localStr[] = "Hello world!";` instead, now it prints garbage. What is the magic behind that? – Marc Sep 11 '21 at 07:41
  • @Marc That's an array. If you define it within a function, then it has automatic storage. – eerorika Sep 11 '21 at 07:42
  • Oh, yes of course it is... At least I understood both cases now. So to generalize, one can say that a char pointer to a static string literal always remains valid throughout the whole program? – Marc Sep 11 '21 at 07:49