Reputation: 403
I have some code:
int *ip = new int;
*ip = 42;
cout << *ip << endl;
cout << ip << endl;
delete ip;
cout << *ip << endl;
cout << ip << endl;
The output is:
42
0x100105400
42
0x100105400
From the value of the pointer and the value on the memory where it pointed to, I think I can't know whether the bit of memory in the heap pointed by ip
is free?
I know that if I were to add delete ip;
again after my code, the compiler will throw an error. That will be a good evidence that the memory is free.
But, how can I test whether it's free peacefully, so that I can use it as a condition to decide further operations in my code?
Upvotes: 0
Views: 1380
Reputation: 56557
You probably have some design issues if your code depends on whether specific memory is free or not. But, if you indeed want to test, you can overload operator new
and operator delete
(and their corresponding array/class versions) so they keep track of which memory locations are allocated in some global data structure available to your program. Here is some toy example (live on ideone.com) which defines a placement new
that keeps track of what memory (and size) was allocated.
#include <iostream>
#include <map>
std::map<void*, std::size_t> memory; // globally allocated memory map
struct tag {}; // tag for placement new's so we don't overload the global ones
void* operator new(std::size_t size, const tag&)
{
void* addr = malloc(size);
memory[addr] = size;
return addr;
}
void* operator new[](std::size_t size, const tag&) // for arrays
{
return operator new(size, tag());
}
void operator delete(void *p) noexcept
{
memory.erase(p);
free(p);
}
void operator delete[](void *p) noexcept // for arrays
{
operator delete(p);
}
void display_memory()
{
std::cout << "Allocated heap memory: " << std::endl;
for (auto && elem : memory)
{
std::cout << "\tADDR: " << elem.first << " "
<< "SIZE: " << elem.second << std::endl;
}
}
bool is_allocated(void* p)
{
return (memory.find(p) != memory.end());
}
int main()
{
int *p = new(tag()) int[10];
char *c = new(tag()) char;
// test if p is allocated
std::cout << std::boolalpha << "Allocated: "
<< is_allocated(p) << std::endl;
// display the allocated memory
display_memory();
// remove p
delete[] p;
// test again if p is allocated
std::cout << std::boolalpha << "Allocated: "
<< is_allocated(p) << std::endl;
display_memory();
// remove c
delete c;
display_memory();
}
EDIT: I realized that there may be some issues with the code above. In the function
void operator delete(void *p) noexcept
{
memory.erase(p);
free(p);
}
memory.erase(p)
calls also operator delete
, so you may end up with some nasty recursion (for some reason, the code above only enters the recursion once). A fix is to write a custom allocator for the std::map memory
that uses malloc/free
instead of global operator new/delete
.
Upvotes: 1
Reputation: 16026
For the sake of completeness, here is a link to Doug Lea's free store implementation page: http://g.oswego.edu/dl/html/malloc.html.
In the first diagram, the memory layout, you can see that the user data of each allocation chunk is preceded by bookkeeping information which includes an "in-use" flag. The chunks form a linked list: From a given chunk one can reach all chunks after that (and, actually, before that as well).
Knowing data size and alignment requirements one can guess on a given system where that information may be relative to the returned pointer; on my x86_64-pc-cygwin the status bytes are the 8 bytes before the returned address, and the "in use" flag is the 2-bit of the first byte.
While that looks promising, there is a catch: Poking around I could see that a delete() coalescs adjacent chunks so that the bookkeeping information is only valid at the beginning of the fused chunks: If you have two pointers p1, p2 which point to memory in the free store which was allocated in sequence, and you delete in order again, the memory at p2's bookkeeping location is not refreshed to reflect that p2 was deallocated. Instead, the bookkeeping region before p1 now indicates that the following memory chunk has the size of the single chunks combined.
In order to find out whether the memory a given pointer points to has been freed, one would have to traverse the linked list of memory blocks, presumably starting at a dummy object which was allocated before anything else. Certainly possible, but not desirable and not robust. @vsoftco's idea is much better.
Last not least: People who always return int from main will likely bemoan that all of this is UB. They are right.
Upvotes: 0
Reputation: 4452
But, how can I test whether it's free peacefully, so that I can use it as a condition to decide further operations in my code?
Freeing memory doesn't change the value of the pointer, and will not necessarily change the memory it points to (which you should not try to access once it's freed anyway).
What you should do is set the pointer to nullptr
when deleting it, then you can check if it's nullptr
or not to see if it's been freed.
Example:
int* ip = new int;
*ip = 42;
delete ip;
ip = nullptr;
// ...
if (ip) {
delete ip;
ip = nullptr;
}
Upvotes: 0