-1

Let's say i have the following structs:

struct Foo
{
  int val;
};
struct Test
{
  Foo *bar;
};

and I wanted to create a Test struct:

Test get_test()
{
  Test test;
  Foo foo;
  foo.val = 10;
  test.bar = &foo;

  cout << "INIT: " << test.bar->val << endl;

  return test;
}

int main()
{
    Test test = get_test();
    cout << "AFTER: " << test.bar->val << endl;
    return 0;
}

The output is the following:

INIT: 10
AFTER: 32723

I tried to do this differently:

Test get_test()
{
  Test test;
  Foo *foo;
  foo->val = 10;
  test.bar = foo;

  cout << "INIT: " << test.bar->val << endl;

  return test;
}

But this gave me a SIGSEGV (Address boundary error)

From my limited understanding, I believe it is because in get_test() foo is a temporary variable, so the reference doesn't mean anything. How can I do this properly?

dwib
  • 583
  • 4
  • 19
  • Does this answer your question? [Can a local variable's memory be accessed outside its scope?](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) – 273K Apr 01 '20 at 16:02
  • That explains really well why it doesn't work, but i'm still confused on how I would do this properly – dwib Apr 01 '20 at 16:06
  • @S.M. Correct me if I’m wrong — the relevant concept is not scope, but duration. Scope matters at compile-time; it defines which parts of the code can see which identifiers. Duration matters at runtime; it defines when objects are created and destroyed. Access something after it’s destroyed, it might have the value you expect, or something else; no guarantees either way. – Tom Zych Apr 01 '20 at 16:32

1 Answers1

1

You are on the right track. In your first example, once get_test returns, foo does not exist anymore, and accessing the address where it was is undefined behavior. The same happens in your second try, but here the problem is in get_test itself. You declare Foo* foo;, but never assign it to anything, which means that the variable foo is pointing to some random address. Accessing it is undefined behavior. Try this as your get_test-function:

Test get_test()
{
  Test test;
  Foo *foo = new Foo();
  foo->val = 10;
  test.bar = foo;

  cout << "INIT: " << test.bar->val << endl;

  return test;
}

Here, we allocate foo with new, so it is allocated on the heap, and will remain until you call delete on it. This means you need to make sure to delete it once you are done with it or you will have a memory-leak. In c++14, you could also do it using std::unique_ptr:

struct Test
{
  std::unique_ptr<Foo> bar;
};
{
  Test test;
  std::unique_ptr<Foo> foo = std::make_unique<Foo>();
  foo->val = 10;
  test.bar = std::move(foo);

  cout << "INIT: " << test.bar->val << endl;

  return test;
}

std::unique_ptr will take care of deleting foo once it goes out of scope (when test is destroyed), and you don't have to worry about memory-leaks (but you cannot copy a std::unique_ptr, so you will have to std::move it). std::unique_ptr is available since c++11, std::make_unique since c++14. You will also need to #include <memory> to be able to use them. Check out this link to learn more about the difference between heap and stack, and this one to learn more about std::unique_ptr and move-semantics.

melk
  • 530
  • 3
  • 9