February 03, 2018 Strange behavior when appending pointers of structs to arrays | ||||
|---|---|---|---|---|
| ||||
Given the following code:
import std.stdio : writeln;
---
struct Foo
{
Foo*[] children;
void value()
{
Foo bar = Foo();
version (bad1)
{
this.children ~= &bar;
}
version (bad2)
{
this.children.length += 1u;
this.children[$-1] = &bar;
}
}
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
{
foo.value();
}
foo.bark();
}
---
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
C:\Users\Jonathan\Desktop\bug>dmd .\bug.d -version=bad2
C:\Users\Jonathan\Desktop\bug>bug.exe
children: 1
grandchildren: 1
C:\Users\Jonathan\Desktop\bug>dmd .\bug.d -version=good
C:\Users\Jonathan\Desktop\bug>bug.exe
children: 1
grandchildren: 0
Why?
| ||||
February 04, 2018 Re: Strange behavior when appending pointers of structs to arrays | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M. Wilbur | 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; | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply