0

I'm trying to assign a string to a struct value, which works, but the value somehow links to the variable:

string tmp;
struct test_struct{
    const char *server, *user; 
};
test_struct test;

tmp = "foo";
test.server = tmp.c_str();
cout << test.server << endl;

tmp = "bar";
test.user = tmp.c_str();
cout << test.user << endl;

cout << "" << endl;

cout << test.server << endl;
cout << test.user << endl;

The output is:

foo
bar

bar
bar

Can someone please explain why this is happening?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 3
    Ask yourself -- where in memory do those pointers point? How long are those pointers valid? Using raw pointers means tracking ownership and tracking lifetime of data that is pointed to. A simpler approach would be to use `std::string` instead of raw pointers. – Drew Dormann Nov 16 '21 at 22:54
  • 8
    Note that accessing `test.server` after execuing `tmp = "bar";` leads to undefined behavior. The pointer returned by [`std::string::c_str`](https://en.cppreference.com/w/cpp/string/basic_string/c_str) becomes invalidated as soon as you invoke a non-const member function of the string (with a few exceptions). That includes [`operator=`](https://en.cppreference.com/w/cpp/string/basic_string/operator%3D) – Brian61354270 Nov 16 '21 at 22:54
  • 1
    `server` and `user` store the address of whatever sequence of characters are in `tmp`. If you change the contents of `tmp`, two things could happen: 1) the new value is written at the same memory location, or 2) the new value is written at a new memory location. If, after `tmp` is changed, you read from `server` or `user`, which still point to the original address, two things could happen: A) you read the new value, or B) you can read basically whatever. But since 1 and 2 are implementation details over which you have no control, you should only consider your code yields B behaviour. – rturrado Nov 16 '21 at 23:26

1 Answers1

2
struct test_struct{
    const char *server, *user; 
};
test_struct test;

Okay, so test.server is a pointer.

test.server = tmp.c_str(); // 1
cout << test.server << endl; // 2

tmp = "bar"; // 3
test.user = tmp.c_str();
cout << test.user << endl;

cout << "" << endl;

cout << test.server << endl; // 4

In 1, you set test.server to point to the contents of tmp. In 2, you output what test.server points to, which is fine.

In 3, you modify the contents of tmp. So now, test.server points to something that no longer exists -- the former contents of tmp.

In 4, you output what test.server points to. But that was the contents of tmp back at 1, which you destroyed in 3.

You cannot output something that you obliterated.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278