2

Is there any way to distinguish two following situations at run time:

double ptr * = new double(3.14159);
double variable = 3.14159

double * testPtr_1 = ptr;
double * testPtr_2 = &variable;

delete testPtr_1  // fine... 
delete testPtr_2  // BIG RUN TIME ERROR !!!

I have find myself in situation in with I need to call delete operator for some unknown pointer. The pointer can point to anywhere (to a "local" variable or to dynamically allocated variable).

How can I find out where my "unknown" pointer points, and therefore choose when to and when not to call operator delete on it




EDIT: Ok I see that everyone is pointing me to the smart pointers, but what if I am trying to write my own set of smart pointers (that is The reason behind my question)?

PatrykB
  • 1,579
  • 1
  • 15
  • 24
  • @George You unmasked me.., I am writing my own set of smart pointers, and that is why I am trying to distinguish this both situations. – PatrykB Mar 28 '17 at 16:18
  • This is precisely the reason (or one of the reasons) why smart pointers (or annotated pointers) exist. – Emil Laine Mar 28 '17 at 16:18
  • @cukier haha, you could implement both weak and strong pointers. – George Mar 28 '17 at 16:19
  • This is already answered. Hm. having problems putting in links. but it's here -> stackoverflow.com/questions/3230420/how-to-know-if-a-pointer-points-to-the-heap-or-the-stack The answer given here is the same as the answer there. One can't do this reliably. – Display name Mar 28 '17 at 16:20
  • Smart pointers don't solve the specific issue you asked about. –  Mar 28 '17 at 16:21
  • This is an implementation issue. Some memory managers may provide some sort of marker that indicate that memory was allocated by them (and I know some do, but they're typically designed for debugging), however, none are required to. So, no, there's really isn't a good way of doing this short of overriding new to provide a marker. – Donnie Mar 28 '17 at 16:24
  • Try this: https://www.codeproject.com/articles/15351/implementing-a-simple-smart-pointer-in-c. It is a tutorial on smart pointers. – Display name Mar 28 '17 at 16:26
  • Possible duplicate of [How to know if a pointer points to the heap or the stack?](http://stackoverflow.com/questions/3230420/how-to-know-if-a-pointer-points-to-the-heap-or-the-stack) – Jonas Schäfer Mar 28 '17 at 16:28

5 Answers5

6

There is no way to test if a pointer is pointing to a memory area that would be valid to delete. Moreover,

  • There is no way to tell between pointers that must be freed with delete vs. delete[],
  • There is no way to tell between the pointers that have been freed and pointers that have not been freed,
  • There is no way to tell among pointers to an automatic variable, pointers to static variable, and pointers to dynamically allocated blocks.

The approach that you should take is tracking allocations/deallocations by some other means, such as storing flags along with your pointers. However, this is rather tedious: a much better practice is to switch to smart pointers, which would track resources for you.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Ok i haven't been completely honest when i asked my question... I am trying to write my own set of smart pointers. So basically you are telling that I need to track resource? Are You aware of example implementation? – PatrykB Mar 28 '17 at 16:28
  • 1
    @cukier9a7b5 In that case do like the standard smart pointers do and take a deleter which you default to just using `delete`. That way if the user of the class wants to pass in a pointer to a stack object they can specify a do nothing deleter and you smart pointer will use that and, well, do nothing. – NathanOliver Mar 28 '17 at 16:30
5

You need to set some better coding practices for yourself (or for your project).

Especially since most platforms have, at the very least, a C++11-compliant compiler, there's no reason not to be using the following paradigm:

  • Raw Pointers (T*) should ONLY be used as non-owning pointers. If you receive a T* as the input for a function or constructor, you should assume you have no responsibility for deleting it. If you have an instance or local variable that is a T*, you should assume you have no responsibility for deleting it.
  • Unique Pointers (std::unique_ptr<T>) should be used as single-ownership pointers, and in general, these should be your default go-to choice for any situation where you need to dynamically allocate memory. std::make_unique<T>() should be preferred for creating any kind of Unique Pointer, as this prevents you from ever seeing the raw pointer in use, and it prevents issues like you described in your original post.
  • Shared Pointers (std::shared_ptr<T> and std::weak_ptr<T>) should ONLY be used in situations where it is logically correct to have multiple owners of an object. These situations occur less often than you think, by the way! std::make_shared<T>() is the preferred method of creating Shared Pointers, for the same reasons as std::make_unique, and also because std::make_shared can perform some optimizations on the allocations, improving performance.
  • Vectors (std::vector<T>) should be used in situations where you need to allocate multiple objects into heap space, the same as if you called new T[size]. There's no reason to use pointers at all except in very exotic situations.

It should go without saying that you need to take my rules of "ONLY do 'x'" with a grain of salt: Occasionally, you will have to break those rules, and you might be in a situation where you need a different set of rules. But for 99% of use-cases, those rules are correct and will best convey the semantics you need to prevent memory leaks and properly reason about the behavior of your code.

Xirema
  • 19,889
  • 4
  • 32
  • 68
  • That is what I was looking for. Your post explains exactly what I need to do in order to write pretty neat lib. Thank you very much. But also you have "opened my eyes" because now I see that my task is a bit pointless - all I can do is to "recreate" parts of C++11 standard smart pointers. – PatrykB Mar 28 '17 at 16:42
  • 1
    @cukier9a7b5 Yeah. It's a good learning experience if you want to spend time writing your own smart pointers, for the purposes of learning how they work and how to responsibly manage memory, but for any kind of proper project, professional or otherwise, you're better off just sticking with the smart pointers provided by the standard library, as those have been vetted and tested many times over to ensure correct and safe behavior. – Xirema Mar 28 '17 at 16:44
4

You cannot.

Avoid raw pointers and use smart pointers, particularly std::unique_ptr. It conveys clearly who is responsible for deleting the object, and the object will be deleted when the std::unique_ptr goes out of scope.

When creating objects, avoid using new. Wrap them in a smart pointer directly and do not take addresses of anything to wrap it in a smart pointer. This way, all raw pointers will never need freeing and all smart pointers will get cleaned up properly when their time has come.


Okay, some things you can distinguish in a very platform-specific, implementation-defined manner. I won’t go into details here, because it’s essentially insane to do (and, again, depends on the platform and implementation), but you are asking for it.

  1. Distinguish local, global and heap variables. This is actually possible on many modern architectures, simply because those three are different ranges of the address space. Global variables live in the data section (as defined by the linker and run-time loader), local variables on the stack (usually at the end of the address space) and heap variables live in memory obtained during run-time (usually not at the end of the address space and of course not overlapping the data and code sections, a.k.a. "mostly everything else"). The memory allocator knows which range that is and can tell you details about the blocks in there, see below.

  2. Detect already-freed variables: you can ask the memory allocator that, possibly by inspecting its state. You can even find out when a pointer points into a allocated region and then find out the block to which it belongs. This is however probably computationally expensive to do.

Distinguishing heap and stack is a bit tricky. If your stack grows large and your program is running long and some piece of heap has been returned to the OS, it is possible that an address which formerly belonged to the heap now belongs to the stack (and the opposite may be possible too). So as I mentioned, it is insane to do this.

Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69
  • 2
    Of course, smart pointers would have exactly the same issues as raw pointers in the OP's case. –  Mar 28 '17 at 16:19
  • @Jonas Wielicki Thank you, for your answer, one of best around here. But I have one question. You said that on most of modern architectures heap and stack share address space. Am I right to assume this is because pointers on 64 bit architecture systems use 8 Bytes to map memory? And that is enough to map `137438953472 GB` of data? ( i have assumed that each of 2^64 addresses points to a storage of 8 Bytes) – PatrykB Mar 28 '17 at 17:06
  • @cukier9a7b5 Pretty sure the addressing is still one-to-one on a typical system. Otherwise addressing individual bytes of a `char` array would be exceptionally tricky. – user4581301 Mar 28 '17 at 17:30
2

You can't reliably. This is why owning raw pointers are dangerous, they do not couple the lifetime to the pointer but instead leave it up to you the programmers to know all the things that could happen and prepare for them all.

This is why we have smart pointers now. These pointers couple the life time to the pointer which means the pointer is only deleted once it is no longer in use anywhere. This makes dealing with pointer much more manageable.

The cpp core guildlines suggests that a raw pointer should never be deleted as it is just a view. You are just using it like a reference and it's lifetime is managed by something else.


Ok I see that everyone is pointing me to the smart pointers, but what if I am trying to write my own set of smart pointers (that is The reason behind my question)?

In that case do like the standard smart pointers do and take a deleter which you default to just using delete. That way if the user of the class wants to pass in a pointer to a stack object they can specify a do nothing deleter and you smart pointer will use that and, well, do nothing. This puts the onus on the person using the smart pointer to tell the pointer how to delete what it points to. Normally they will never need to use something other than the default but if they happen to use a custom allocator and need to use a custom deallocator they can do so using this method.

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • "a raw pointer should never be deleted " - that would make it impossible to implement smart pointers. –  Mar 28 '17 at 16:23
  • @NeilButterworth The guidelines are not meant for system internal code. Of course the implementation of `unique_ptr` is going to need to use `new` and `delete` but that doesn't mean we need to in our code. We abstract that behind facilities that have been tested and are known to work. – NathanOliver Mar 28 '17 at 16:25
  • That assumes that unique and shared pointers are the only kinds of pointers one could possibly want. I'm not sure if that's true. –  Mar 28 '17 at 16:27
  • @NeilButterworth It’s only a "should not", not a "must not". Thinking RFC 2119, if there’s a very good reason to do it, you should of course still do it. – Jonas Schäfer Mar 28 '17 at 16:27
1

Actually you can. But memory overhead occurs.

You overload new and delete operator and then keep track of allocations and store it somewhere(void *)

#include<iostream>
#include<algorithm>
using namespace std;

void** memoryTrack=(void **)malloc(sizeof(void *)*100); //This will store address of newly allocated memory by new operator(really malloc)
int cnt=0;//just to count

//New operator overloaded
void *operator new( size_t stAllocateBlock ) {  

    cout<<"in new";
    void *ptr = malloc(stAllocateBlock); //Allocate memory using malloc
    memoryTrack[cnt] = ptr;//Store it in our memoryTrack
    cnt++;          //Increment counter
    return ptr;     //return address generated by malloc

}  

void display()
{
    for(int i=0;i<cnt;i++)
        cout<<memoryTrack[i]<<endl;
}
int main()
{
    double *ptr = new double(3.14159);
    double variable = 3.14159;

    double * testPtr_1 = ptr;
    double * testPtr_2 = &variable;

    delete testPtr_1; // fine... 
    delete testPtr_2;
    return 0;
}

Now the most important function(You will have to work on this because it is not complete)

void operator delete( void *pvMem )
{
    //Just printing the address to be searched in our memoryTrack
    cout<<pvMem<<endl;
    //If found free the memory
    if(find(memoryTrack,memoryTrack+cnt,pvMem)!=memoryTrack+cnt)
    {
        //cout<<*(find(memoryTrack,memoryTrack+cnt,pvMem));
        cout<<"Can be deleted\n";
        free (pvMem);
        //After this make that location of memoryTrack as NULL
        //Also keep track of indices that are NULL
        //So that you can insert next address there 
        //Or better yet implement linked list(Sorry was too lazy to do)
    }
    else
        cout<<"Don't delete memory that was not allocated by you\n";

}

Output

in new
0xde1360
0xde1360
Can be deleted
0xde1360
0x7ffe4fa33f08
Dont delete memory that was not allocated by you
0xde1360

Important Node

  • This is just basics and just code to get you started
  • Open for others to edit and make necessary changes/optimization
  • Cannot use STL, they use new operator(if some can implement them please do,it would help to reduce and optimize the code)
Sniper
  • 1,428
  • 1
  • 12
  • 28