[Dlang-internal] Strange behavior when appending pointers of structs to arrays

Mike Parker aldacron at gmail.com
Sun Feb 4 05:41:04 UTC 2018


For starters, this sort of question belongs in the Learn forum. 
Now to your problem.



> The resulting executable returns the following outputs:
>
> C:\Users\Jonathan\Desktop\bug>dmd .\bug.d -version=bad1
>
> C:\Users\Jonathan\Desktop\bug>bug.exe
> children: 1
> grandchildren: 4203068

Running at run.dlang.io:

children: 1
grandchildren: 10

>
> C:\Users\Jonathan\Desktop\bug>dmd .\bug.d -version=bad2
>
> C:\Users\Jonathan\Desktop\bug>bug.exe
> children: 1
> grandchildren: 1

children: 1
grandchildren: 140722524752048


>
> Why?

Because structs are value types and you're creating them on the 
stack. Anything created on the stack in a function will 
eventually be stomped after the function exits, but with structs 
you also have the fact that the instance is destroyed. You can 
see this by adding a destructor to your Foo type and sprinkling 
some writelns around:

======
import std.stdio : writeln;

struct Foo
{
     Foo*[] children;

     ~this() { writeln("Destroyed!"); }

     void value()
     {
         writeln("Entered value.");
         Foo bar = Foo();
         version (bad1)
         {
             this.children ~= &bar;
         }
         version (bad2)
         {
             this.children.length += 1u;
             this.children[$-1] = &bar;
         }
         writeln("Exiting 'value'");
     }
     void bark()
     {
         writeln("children: ", this.children.length);
         writeln("grandchildren: ", 
this.children[0].children.length);
     }
}

void main ()
{
     Foo foo = Foo();
     version (good)
     {
         Foo bar = Foo();
         foo.children ~= &bar;
     }
     else
     {
         writeln("Entering value.");
         foo.value();
         writeln("value exited.");
     }
     foo.bark();
     writeln("Exiting main");
}
=======

Running bad1 & bad2 will print something like this:

Entering value.
Entered value.
Exiting 'value'
Destroyed!
value exited.
children: 1
grandchildren: 10
Exiting main
Destroyed!

As you can see, the Foo instance created inside value will be 
destroyed when the function exits, so that by the time you call 
foo.bark, children[0] is no longer in a valid state -- hence the 
garbage value for children[0].children.length. If you want bar to 
persist outside of value, you need to allocate it on the heap:

Foo* bar = new Foo;
this.children ~= bar;


More information about the Dlang-internal mailing list