This is a dangling pointer
Foo* p;
{
Foo f;
p = &f;
}
// now p is dangling pointer
However, why is this not a dangling pointer?
Foo* p;
{
Foo f;
...
*p = f;
}
// p still contains the data of f
Is it because in the second case, we are doing a copy? Does this mean I should have written
*p = std::move(f);
if I do not want a copy?
Thank you!
CodePudding user response:
In your 2nd example p
is uninitialized. Dereferencing an uninitialized pointer is Undefined Behavior.
CodePudding user response:
First, make the behaviour well-defined by giving p
a valid value (not leaving things uninitialized is the simplest, cheapest form of bug prevention):
Foo* p = new Foo;
{
...
or
Foo x;
Foo* p = &x;
{
...
Now, in your first piece of code, p
stores the location of f
, which has been destroyed.
This is what makes it a dangling pointer.
In the second, you don't change p
but store the value of f
in *p
, which is the same object as it always was.
Since that object still exists, the pointer is not dangling.
Compare these two snippets:
int x = 0;
int* p = &x;
{
int y = 1;
p = &y;
}
std::cout << "p: " << p << ", &x: " << &x;
and
int x = 0;
int* p = &x;
{
int y = 1;
*p = y;
}
std::cout << "p: " << p << ", &x: " << &x;
and you will see that the first prints two different addresses, and the second prints the same address twice.
CodePudding user response:
The distinction - if any - depends on your definition of "dangling pointer". And it is pretty academic.
Discussion of one possible definition. One possible definition of a dangling pointer is "a pointer that points to data which is no longer valid".
By that definition, your first example;
Foo* p; { Foo f; p = &f; } // now p is dangling pointer
results in p
being a dangling pointer, because it points at the object f
, and f
ceases to exist at the end of its enclosing scope (i.e. the }
). Since p
still exists, and its value is not changed in the process of f
ceasing to exist, p
now points at an object that no longer exists.
By that same definition, your second example;
Foo* p; { Foo f; ... *p = f; } // p still contains the data of f
does not produce a dangling pointer, since p
never pointed at a valid object. In fact, p
is uninitialised, so simply accessing its value produces undefined behaviour and its value (if any) is indeterminate. Dereferencing (i.e. evaluating *p
) cannot be done without evaluating p
so the expression *p
and the statement *p = f
both give undefined behaviour. Doing anything with *p
after the }
causes undefined behaviour (just as it does in your first example).
With this definition, The distinction of p
being a dangling pointer in the first example and not in the second is a bit academic. The difference is that, in the first case, p
is considered a dangling pointer because it no longer points at a valid object but, in the second case, p
is not considered a dangling pointer because it never pointed at a valid object. Practically, the distinction makes no difference, since doing anything with *p
gives undefined behaviour.
Discussion of a second possible definition A second possible definition of a dangling pointer is subtly different to the first "a pointer that points at invalid data".
By this second definition, both of your examples produce a dangling pointer. The first causes p
to point at an object that no longer exists - so it points at invalid data. In the second example, p
never pointed at a valid object - so it also points at invalid data. By this definition of a dangling pointer, p
is a dangling pointer after the }
in both examples because it now points at an invalid object.
The distinction under this second definition is still academic. Simply evaluating *p
(let alone using it to change the invalid object p
points at by, say, *p = some_Foo
) gives undefined behaviour.