1

I believe what I have just experienced is called "undefined behavior", but I'm not quite sure. Basically, I had an instance declared in an outer scope that holds addresses of a class. In the inner level I instantiated an object on the stack and stored the address of that instance into the holder.

After the inner scope had escaped, I checked to see if I could still access methods and properties of the removed instance. To my surprise it worked without any problem.

Is there a simple way to combat this? Is there a way I can clear deleted pointers from the list?

example:

std::vector<int*> holder; 
{
    int inside = 12;

    holder.push_back(&inside);
}
cout << "deleted variable:" << holder[0] << endl;
10
  • I believe this is undefined behavior; since the runtime hadn't swapped out the page in memory or allocated a new stack frame in the same place with a variable that overwrote inside. Commented Jul 28, 2015 at 3:37
  • To clarify, in this example you use the stack-allocated variable inside, however you say in your question that your object was allocated on the heap. If you heap-allocate an object, it stays around until you call delete on it, therefore there is no undefined behavior. If you want to simulate heap-allocation, change int inside = 12; in your example to int* inside = new int; *inside = 12; (with the caveat that later you need to delete it. Commented Jul 28, 2015 at 3:41
  • Is there a good reason to put the address into the list? Because one easy fix is to refrain from doing so. Commented Jul 28, 2015 at 3:42
  • I guess I'm over complicating my code if I'm trying to fight this problem. I shouldn't be adding non-pointers anyhow if they're going to be transferring in and out of scope.. Commented Jul 28, 2015 at 3:44
  • The example is oversimplified. It does not demonstrate the question. Also, you never mentioned: did you free the instance from the heap? Commented Jul 28, 2015 at 3:47

2 Answers 2

3

Is there a simple way to combat this?

Sure, there are a number of ways to avoid this sort of problem.

The easiest way would be to not use pointers at all -- pass objects by value instead. i.e. In your example code, you could use a std::vector<int> instead of a std::vector<int *>.

If your objects are not copy-able for some reason, or are large enough that you think it will be too expensive to make copies of them, you could allocate them on the heap instead, and manage their lifetimes automatically using shared_ptr or unique_ptr or some other smart-pointer class. (Note that passing objects by value is more efficient than you might think, even for larger objects, since it avoids having to deal with the heap, which can be expensive... and modern CPUs are most efficient when dealing with contiguous memory. Finally, modern C++ has various optimizations that allow the compiler to avoid actually doing a data copy in many circumstances)

In general, retaining pointers to stack objects is a bad idea unless you are 100% sure that the pointer's lifetime will be a subset of the lifetime of the stack object it points to. (and even then it's probably a bad idea, because the next programmer who takes over the code after you've moved on to your next job might not see this subtle hazard and is therefore likely to inadvertently introduce dangling-pointer bugs when making changes to the code)

After the inner scope had escaped, I checked to see if I could still access methods and properties of the removed instance. To my surprise it worked without any problem.

That can happen if the memory where the object was hasn't been overwritten by anything else yet -- but definitely don't rely on that behavior (or any other particular behavior) if/when you dereference an invalid pointer, unless you like spending a lot of quality time with your debugger chasing down random crashes and/or other odd behavior :)

Is there a way I can clear deleted pointers from the list?

In principle, you could add code to the objects' destructors that would go through the list and look for pointers to themselves and remove them. In practice, I think that is a poor approach, since it uses up CPU cycles trying to recover from an error that a better design would not have allowed to be made in the first place.

Btw this is off topic but it might interest you that the Rust programming language is designed to detect and prevent this sort of error by catching it at compile-time. Maybe someday C++ will get something similar.

Sign up to request clarification or add additional context in comments.

Comments

0

There is no such thing as deleted pointer. Pointer is just a number, representing some address in your process virtual address space. Even if stack frame is long gone, memory, that was holding it is still available, since it was allocated when thread started, so technically speaking, it is still a valid pointer, valid in terms, that you could dereference it and get something. But since object it was pointing is already gone, valid term will be dangling pointer. Moral is that if you have pointer to the object in the stack frame, there is no way to determine is it valid or not, not even using functions like IsBadReadPtr (Win32 API just for example). The best way to prevent such situations is avoid returning and storing pointers to the stack objects.

However, if you wish to track your heap allocated memory and automatically deallocate it after it is no longer used, you could utilize smart pointers (std::shared_ptr, boost::shared_ptr, etc).

5 Comments

I think it's more accurate to call it a dangling pointer, the lifetime of the object had expired but he foolishly saved its address in a std::vector.
@Blastfurnace Agreed. Thanks)
If an object no longer exists at the location a pointer used to point to, then it is an invalid pointer (not a valid one). Dereferencing such a pointer causes undefined behaviour. Also, pointers are not just numbers. There might not be a virtual address space or a stack frame, in general.
Could you provide an example of such situation?
@Ari0nhh: On some systems, large allocations are handled directly by the OS. Freeing those allocations returns the memory straight to the OS. Subsequent use of those pointers will segfault (a for of Undefined Behavior). As for pointers not being numbers, the common example is pointers to members.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.