Click to See Complete Forum and Search --> : pointer


Vije
October 19th, 2002, 03:51 AM
How could I know if a pointer is on the stack or on heap?!
(How could I know if a pointer was allocated with new or with a reference?)

JamesSchumacher
October 19th, 2002, 11:22 AM
Although I don't understand why you would need to know this, as unless you are receiving a pointer from a function that you did not write, you should know if the data pointed at by the pointer is on the stack or the heap.

Why would you need to know?

alexey_1979
October 20th, 2002, 07:30 AM
it's simple!

call _msize. if it return 0 or something like that - object in stack!

Note. It's a Microsoft specific.

stober
October 20th, 2002, 09:14 AM
When compiled for debug, _msize() produces an ASSERT if the object was not allocated with malloc() family of functions.

willchop
October 20th, 2002, 10:24 AM
James,
(How could I know if a pointer was allocated with new or with a reference?)
I believe you mean an address operator - not a reference.

That being said, I don't know any generic way you can tell whether a
pointer contains an address to stack or heap memory.

regards, willchop

Paul McKenzie
October 20th, 2002, 11:55 AM
There is no generic C++ way to do this. This is covered in one of Scott Meyer's Effective C++ books (Effective C++ or More Effective C++, I don't remember which one).

Since C++ doesn't assume that there is even a "stack", the answer to your question is highly system, compiler, and OS dependent.

Regards,

Paul McKenzie

JamesSchumacher
October 20th, 2002, 11:56 AM
I didn't post this question.

Not a big deal - but you typed James, and then quoted the poster of the question.

willchop
October 20th, 2002, 04:03 PM
Sorry James!

-willchop

willchop
October 21st, 2002, 02:45 PM
it's simple!

call _msize. if it return 0 or something like that
This function appears to only keep track of the actual
pointer to the memory allocated by malloc - not valid addresses
within that block. For this reason the function may not provide
accurate results.

For example:

char* buffer = static_cast<char*>(malloc(1000 * sizeof(char)));
if (!buffer) return;

char* mid_buffer = buffer + 499;

printf("sizeof buffer: %d\n", _msize(buffer));
printf("sizeof mid_buffer: %d\n", _msize(mid_buffer));

free(buffer);

The mid_buffer is pointing to memory that was dynamically
allocated, but _msize() fails to report so.

regards, willchop

Axter
October 21st, 2002, 04:29 PM
(How could I know if a pointer was allocated with new or with a reference?)


You could either create a new operator for the specific object you want to check, or if it's for multiple objects, you could over write the global new operator.

Within your new operator, you can update a global vector of pointers, and then LATER, check the vector to see if your pointer is listed in the vector.

Axter
October 21st, 2002, 04:56 PM
Here's an example:

#include <iterator>
#include <new>
#include <map>


class foo {
public:
int x;
void* operator new(size_t)
{
foo* ptr = ::new foo;
FooPtrs[ptr] = 0;
return ptr;
}
void operator delete(void* ptr)
{
FooPtrs.erase((foo*)ptr);
::delete ptr;
}
void* operator new[] (size_t s)
{
foo* ptr = ::new foo[s];
for(foo* p = ptr;p < ptr+s;++p)
FooPtrs[p]=s;
return ptr;
}
void operator delete[] (void* ptr)
{
size_t s = FooPtrs[(foo*)ptr];
for(foo* p = (foo*)ptr;p < (foo*)ptr+s;++p)
FooPtrs.erase(p);
}
static std::map<const foo*, size_t> FooPtrs;
static bool IsCreatedByNew(const foo* ptr)
{
return (FooPtrs.find(ptr) != FooPtrs.end());
}
};

std::map<const foo*, size_t> foo::FooPtrs;


int main(int argc, char* argv[])
{
foo *pXa = new foo[5];
foo *pX = new foo;
foo NotPtr;
foo NotPtrA[5];
bool t1 = foo::IsCreatedByNew(pXa);
pXa++;
bool t2 = foo::IsCreatedByNew(pXa++);
bool t3 = foo::IsCreatedByNew(pX);
bool t4 = foo::IsCreatedByNew(&NotPtr);
bool t5 = foo::IsCreatedByNew(&NotPtrA[0]);
bool t6 = foo::IsCreatedByNew(&NotPtrA[2]);

delete [] pXa;
delete pX;
bool t7 = foo::IsCreatedByNew(pX);
bool t8 = foo::IsCreatedByNew(pXa++);
return 0;
}

Axter
October 21st, 2002, 04:58 PM
Correction:


#include <iterator>
#include <new>
#include <map>

class foo {
public:
void* operator new(size_t)
{
foo* ptr = ::new foo;
FooPtrs[ptr] = 0;
return ptr;
}
void operator delete(void* ptr)
{
FooPtrs.erase((foo*)ptr);
::delete ptr;
}
void* operator new[] (size_t s)
{
foo* ptr = ::new foo[s];
for(foo* p = ptr;p < ptr+s;++p)
FooPtrs[p]=s;
return ptr;
}
void operator delete[] (void* ptr)
{
size_t s = FooPtrs[(foo*)ptr];
for(foo* p = (foo*)ptr;p < (foo*)ptr+s;++p)
FooPtrs.erase(p);
}
static std::map<const foo*, size_t> FooPtrs;
static bool IsCreatedByNew(const foo* ptr)
{
return (FooPtrs.find(ptr) != FooPtrs.end());
}
};

std::map<const foo*, size_t> foo::FooPtrs;


int main(int, char**)
{
foo *pXa = new foo[5];
foo *pX = new foo;
foo NotPtr;
foo NotPtrA[5];
bool t1 = foo::IsCreatedByNew(pXa);
pXa++;
bool t2 = foo::IsCreatedByNew(pXa++);
bool t3 = foo::IsCreatedByNew(pX);
bool t4 = foo::IsCreatedByNew(&NotPtr);
bool t5 = foo::IsCreatedByNew(&NotPtrA[0]);
bool t6 = foo::IsCreatedByNew(&NotPtrA[2]);

delete [] pXa;
delete pX;
bool t7 = foo::IsCreatedByNew(pX);
bool t8 = foo::IsCreatedByNew(pXa++);
return 0;
}

Vije
October 21st, 2002, 07:06 PM
thank you all
Thanx...It works!

How about a generic method? a template seems to be more accurate here...

JamesSchumacher
October 21st, 2002, 07:06 PM
Originally posted by Axter
Correction:


void* operator new(size_t)
{
foo* ptr = ::new foo;
FooPtrs[ptr] = 0;
return ptr;
}


I hope the compiler is smart enough not to call the constructor twice.

First of all, you don't check if calling global new fails or not. But in this case, it's not safe to do what this overload of new does. If global new succeeds - the constructor is called for the object. This could be disastrous if the class allocates memory in it's default constructor. Because when the overloaded new returns and succeeded - it's calls the constructor again. As you know, the constructor is the first function called in an object's life - therefore the member variable (the pointer) that points at memory is thought of as 'garbage' by the 2nd call to the constructor. Therefore you have a memory leak.

Axter
October 21st, 2002, 11:31 PM
If global new succeeds - the constructor is called for the object. This could be disastrous if the class allocates memory in it's default constructor. Because when the overloaded new returns and succeeded - it's calls the constructor again. As you know, the constructor is the first function called in an object's life - therefore the member variable (the pointer) that points at memory is thought of as 'garbage' by the 2nd call to the constructor. Therefore you have a memory leak.


You're absolutely right. I assume the constructor would be called only when the global operator is called.

I modified the code to use malloc and free instead. That will prevent the double constructor and double destructor calls.

As this is just an example, I leave it up to the questioner to add extra required validation.


#include <iostream>

#include <iterator>
#include <new>
#include <map>
#include <stdlib.h>

class foo {
public:
foo()
{
++Qty;
std::cout << "Passing through constructor " << Qty << std::endl;
}
~foo()
{
--Qty;
std::cout << "Passing through destructor " << Qty << std::endl;
}
void* operator new(size_t)
{
foo* ptr = (foo*)malloc(sizeof(foo));
FooPtrs[ptr] = 0;
return ptr;
}
void operator delete(void* ptr)
{
FooPtrs.erase((foo*)ptr);
free(ptr);
}
void* operator new[] (size_t s)
{
foo* ptr = (foo*)malloc(sizeof(foo)*s);
for(foo* p = ptr;p < ptr+s;++p)
FooPtrs[p]=s;
return ptr;
}
void operator delete[] (void* ptr)
{
free(ptr);
size_t s = FooPtrs[(foo*)ptr];
for(foo* p = (foo*)ptr;p < (foo*)ptr+s;++p)
FooPtrs.erase(p);
}
static std::map<const foo*, size_t> FooPtrs;
static bool IsCreatedByNew(const foo* ptr)
{
return (FooPtrs.find(ptr) != FooPtrs.end());
}
static int Qty;
};
int foo::Qty = 0;

std::map<const foo*, size_t> foo::FooPtrs;

void SomeFunction()
{
foo *pX = new foo;
foo *pXa = new foo[5];
foo NotPtr;
foo NotPtrA[5];
bool t1 = foo::IsCreatedByNew(pXa);
pXa++;
bool t2 = foo::IsCreatedByNew(pXa++);
bool t3 = foo::IsCreatedByNew(pX);
bool t4 = foo::IsCreatedByNew(&NotPtr);
bool t5 = foo::IsCreatedByNew(&NotPtrA[0]);
bool t6 = foo::IsCreatedByNew(&NotPtrA[2]);

delete pX;
pXa--;
pXa--;
delete [] pXa;
bool t7 = foo::IsCreatedByNew(pX);
bool t8 = foo::IsCreatedByNew(pXa++);
}


int main(int, char**)
{
SomeFunction();
return 0;
}

Axter
October 21st, 2002, 11:36 PM
How about a generic method? a template seems to be more accurate here...


You can make the code more generic by having the new and delete operators call a global function that would update the Ptr map, and create/free memory.

Axter
October 21st, 2002, 11:48 PM
The following is an example using some template functions.


std::map<const void*, size_t> PtrTracker;

template<typename T>
T* GetTrackerMemory(size_t s)
{
T* ptr = (T*)malloc(sizeof(T)*s);
PtrTracker[ptr] = s;
return ptr;
}

template<typename T>
void FreeTrackerMemory(T* ptr)
{
size_t s = PtrTracker[ptr];
for(T* p = ptr;p < ptr+s;++p)
PtrTracker.erase(p);
free(ptr);
}

template<typename T>
bool IsCreatedByNew(const T* ptr)
{
return (PtrTracker.find(ptr) != PtrTracker.end());
}

class foo {
public:
foo()
{
++Qty;
std::cout << "Passing through constructor " << Qty << std::endl;
}
~foo()
{
--Qty;
std::cout << "Passing through destructor " << Qty << std::endl;
}
void* operator new(size_t)
{
return GetTrackerMemory<foo>(1);
}
void operator delete(void* ptr)
{
FreeTrackerMemory((foo*)ptr);
}
void* operator new[] (size_t s)
{
return GetTrackerMemory<foo>(s);
}
void operator delete[] (void* ptr)
{
FreeTrackerMemory((foo*)ptr);
}
static int Qty;
};
int foo::Qty = 0;

void SomeFunction()
{
foo *pX = new foo;
foo *pXa = new foo[5];
foo NotPtr;
foo NotPtrA[5];
bool t1 = IsCreatedByNew(pXa);
pXa++;
bool t2 = IsCreatedByNew(pXa++);
bool t3 = IsCreatedByNew(pX);
bool t4 = IsCreatedByNew(&NotPtr);
bool t5 = IsCreatedByNew(&NotPtrA[0]);
bool t6 = IsCreatedByNew(&NotPtrA[2]);

delete pX;
pXa--;
pXa--;
delete [] pXa;
bool t7 = IsCreatedByNew(pX);
bool t8 = IsCreatedByNew(pXa++);
}


int main(int, char**)
{
SomeFunction();
return 0;
}

Vije
October 22nd, 2002, 12:56 AM
Thank you a lot Axter for your effort.

It's only 1 problem....your code is not so generic...
Why?? If you have more classes ( let's suppose foo2, foo3, foo4 ......) you must modify each of them in order to find out whether an instance of any class is allocated on the heap or not...Am I wrong?

Here is the way a user would be happy to use...
Heap_Test<foo> pXa = new foo[5];
Heap_Test<foo1> pX = new foo;
Heap_Test<foo2> NotPtr;
Heap_Test<foo3> NotPtrA[5];

bool t1 = ::IsCreatedByNew(pXa);
bool t3 = ::IsCreatedByNew(pX);
bool t4 = ::IsCreatedByNew(NotPtr);
bool t5 = ::IsCreatedByNew(NotPtrA[0]);
bool t6 = ::IsCreatedByNew(NotPtrA[2]);

delete [] pXa;
delete pX;
bool t7 = ::IsCreatedByNew(pX);

I think above method involves smart pointers/handle...Unfortunately I cant implement it right now...I hope you will do Axter...

it might be some other better technics...

Vije
October 22nd, 2002, 03:03 AM
"If you want to supply an allocator/deallocator pair that works corectly for derived classes you must either supply a virtual destructor in base class or refrain from using the size t in deallocator"

B. Stroustrup "The C++ Programming Language", 3rd edition,
15.6 Free Store

zdf
October 22nd, 2002, 05:33 PM
Howdy Vije!

It is not clear why you want to discriminate between stack and heap allocated variables. Try the code below; it may help you.

class B
{
private: ~B() {}
public: void release() { delete this; } // always on the heap
};

int main()
{
B* hb = new B; // ok
B sb; // error: destructor not visible
// ...
return 0;
}


Regards,