Is it safe to delete a void pointer?
Asked Answered
Z

13

103

Suppose I have the following code:

void* my_alloc (size_t size)
{
   return new char [size];
}

void my_free (void* ptr)
{
   delete [] ptr;
}

Is this safe? Or must ptr be cast to char* prior to deletion?

Zurn answered 2/6, 2009 at 20:47 Comment(10)
Why are you doing memory management yourself? What data structure are you creating? Needing to do explicit memory management is pretty rare in C++; you should usually use classes that handle it for you from the STL (or from Boost in a pinch).Starknaked
Just for people reading, I use void* variables as parameters for my threads in win c++ ( see _beginthreadex ). Usually they're acutally pointing to classes.Henson
In this case it's a general purpose wrapper for new/delete, that could contain allocation tracking stats or an optimized memory pool. In other cases, I've seen object pointers incorrectly stored as void* member variables, and incorrectly deleted in the destructor without casting back to the appropriate object type. So I was curious about the safety/pitfalls.Blackamoor
For a general purpose wrapper for new/delete you can overload the new/delete operators. Depending what environment you use, you probably get hooks into the memory management to track allocations. If you end up in a situation where you don't know what your are deleting, take it as a strong hint that your design is suboptimal and needs refactoring.Iwo
Since you are already wrapping the allocation and destruction, you could easily cast with no overhead.Worrywart
Can think of loads of reasons why you would want to do this in c++, for example, say you have a database your program needs, it has been exported into a binary format, and you want to be able to load this pre processed database into a continuous block of memory... or if you have your own virtual file system (although I expect these kind of techniques are probably more useful in game development where performance is criticalDeficient
I think there's too much questioning the question instead of answering it. (Not only here, but in all SO)Clarkin
But seriously, @Andrew just use malloc and free directly. That implicitly carries the fact that the constructor/destructor will not be called (as the answers below say) on whatever underlies that void* pointer.Wigeon
Since char does not have a destructor, this will be safe. malloc/free would be faster, but then you forfeit std::bad_alloc.Maramarabel
I use a void* as a context that is passed to me by C# code and whose type will be in thirdparty C++ code implementing a base plugin class I provide. In my case all that was required was to do delete (MyPluginBase*) aPointer and ensure that MyPlugin has a virtual destructor.Stedman
F
28

It depends on "safe." It will usually work because information is stored along with the pointer about the allocation itself, so the deallocator can return it to the right place. In this sense it is "safe" as long as your allocator uses internal boundary tags. (Many do.)

However, as mentioned in other answers, deleting a void pointer will not call destructors, which can be a problem. In that sense, it is not "safe."

There is no good reason to do what you are doing the way you are doing it. If you want to write your own deallocation functions, you can use function templates to generate functions with the correct type. A good reason to do that is to generate pool allocators, which can be extremely efficient for specific types.

As mentioned in other answers, this is undefined behavior in C++. In general it is good to avoid undefined behavior, although the topic itself is complex and filled with conflicting opinions.

Fuegian answered 2/6, 2009 at 21:14 Comment(8)
How is this an accepted answer? It doesn't make any sense to "delete a void pointer" -- safety is a moot point.Tie
"There is no good reason to do what you are doing the way you are doing it." That is your opinion, not fact.Maramarabel
@Maramarabel Provide a counter example where doing what the author of the question wants to do is a good idea in C++.Fuegian
I think this answer is actually mostly reasonable, but I also think that any answer to this question needs to at least mention that this is undefined behavior.Grounder
@Fuegian Try writing a single garbage collector system which is not type specific but simply works. The fact that sizeof(T*) == sizeof(U*) for all T,U suggests that it should be possible to have 1 non templated, void * based garbage collector implementation. But then, when the gc actually has to delete/free a pointer exactly this question arises. To make it work, you either need lambda function destructor wrappers (urgh) or you would need some sort of dynamic "type as data" kind of thing which allows back and forth between a type and something storable.Conventionalize
@Conventionalize I have done. I know that it is hard, but the point is that what the question is asking is undefined behavior. See chromium.googlesource.com/chromium/src/+/master/third_party/… for a good way to do garbage collection in C++.Fuegian
@Fuegian While I focused less on the GC algorithm itself (used an unordered_map<void*,Something>), I figured out a - well - workable if not very memory efficient way to do it: template <class T> struct Foo { void deleter(void*p) { delete reinterpret_cast<T*>(p); } }; Then I stored a pointer to that deleter function into Something, at the transition from typed to untyped. Arrays of T aside, this should be solid and easily extendable..Conventionalize
"the topic itself is complex and filled with conflicting opinions." Not really. Undefined Behavior is not acceptable.It invalidates everything when it occurs. "It depends on "safe."" Again, not really. It is Undefined Behavior. There is no rational definition of "safe" code that permits Undefined Behavior.Telford
P
153

Deleting via a void pointer is undefined by the C++ Standard - see section 5.3.5/3:

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

And its footnote:

This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void

.

Poling answered 2/6, 2009 at 21:15 Comment(3)
No, of course not. It still says it's UB. Even more so, now it states it normatively that deleting void* is UB :)Shoran
Fill the pointed address memory of a void pointer with NULL make any difference for application memory management?Bosquet
This was answered in 2009, is it still true as of C++17/20?Commination
F
28

It depends on "safe." It will usually work because information is stored along with the pointer about the allocation itself, so the deallocator can return it to the right place. In this sense it is "safe" as long as your allocator uses internal boundary tags. (Many do.)

However, as mentioned in other answers, deleting a void pointer will not call destructors, which can be a problem. In that sense, it is not "safe."

There is no good reason to do what you are doing the way you are doing it. If you want to write your own deallocation functions, you can use function templates to generate functions with the correct type. A good reason to do that is to generate pool allocators, which can be extremely efficient for specific types.

As mentioned in other answers, this is undefined behavior in C++. In general it is good to avoid undefined behavior, although the topic itself is complex and filled with conflicting opinions.

Fuegian answered 2/6, 2009 at 21:14 Comment(8)
How is this an accepted answer? It doesn't make any sense to "delete a void pointer" -- safety is a moot point.Tie
"There is no good reason to do what you are doing the way you are doing it." That is your opinion, not fact.Maramarabel
@Maramarabel Provide a counter example where doing what the author of the question wants to do is a good idea in C++.Fuegian
I think this answer is actually mostly reasonable, but I also think that any answer to this question needs to at least mention that this is undefined behavior.Grounder
@Fuegian Try writing a single garbage collector system which is not type specific but simply works. The fact that sizeof(T*) == sizeof(U*) for all T,U suggests that it should be possible to have 1 non templated, void * based garbage collector implementation. But then, when the gc actually has to delete/free a pointer exactly this question arises. To make it work, you either need lambda function destructor wrappers (urgh) or you would need some sort of dynamic "type as data" kind of thing which allows back and forth between a type and something storable.Conventionalize
@Conventionalize I have done. I know that it is hard, but the point is that what the question is asking is undefined behavior. See chromium.googlesource.com/chromium/src/+/master/third_party/… for a good way to do garbage collection in C++.Fuegian
@Fuegian While I focused less on the GC algorithm itself (used an unordered_map<void*,Something>), I figured out a - well - workable if not very memory efficient way to do it: template <class T> struct Foo { void deleter(void*p) { delete reinterpret_cast<T*>(p); } }; Then I stored a pointer to that deleter function into Something, at the transition from typed to untyped. Arrays of T aside, this should be solid and easily extendable..Conventionalize
"the topic itself is complex and filled with conflicting opinions." Not really. Undefined Behavior is not acceptable.It invalidates everything when it occurs. "It depends on "safe."" Again, not really. It is Undefined Behavior. There is no rational definition of "safe" code that permits Undefined Behavior.Telford
L
26

It's not a good idea and not something you would do in C++. You are losing your type info for no reason.

Your destructor won't be called on the objects in your array that you are deleting when you call it for non primitive types.

You should instead override new/delete.

Deleting the void* will probably free your memory correctly by chance, but it's wrong because the results are undefined.

If for some reason unknown to me you need to store your pointer in a void* then free it, you should use malloc and free.

Lemkul answered 2/6, 2009 at 20:52 Comment(6)
You're right about the destructor not being called, but wrong about the size being unknown. If you give delete a pointer that you got from new, it does in fact know the size of the thing being deleted, entirely apart from the type. How it does it isn't specified by the C++ standard, but I've seen implementations where the size is stored immediately before the data that the pointer returned by 'new' points to.Globin
Removed the part about the size, although C++ standard says it is undefined. I know that malloc/free would work though for void* pointers.Lemkul
Don't suppose you have a weblink to the relevant section of the standard? I know that the few implementations of new/delete I've looked at definitely work correctly without type knowledge, but I admit I haven't looked at what the standard specifies. IIRC C++ originally required an array element count when deleting arrays, but no longer does with the newest versions.Globin
Please see @Neil Butterworth answer. His answer should be the accepted one in my opinion.Lemkul
@keysersoze: are you sure new returns data preceded by its size? It would be weird for me to avoid using virtuals because of extra space taked for vptr, and then just pay the same price (the size is probably 4 bytes, like the pointer) for thing, that doesn't look very usable.Poling
@keysersoze: Generally I don't agree with your statement. Just because some implementation did store size before allocated memory doesn't mean that it's a rule.Poling
E
13

Deleting a void pointer is dangerous because destructors will not be called on the value it actually points to. This can result in memory / resource leaks in your application.

Evangelical answered 2/6, 2009 at 21:5 Comment(1)
char does not have a constructor/destructor.Maramarabel
U
8

If you really must do this, why not cut out the middle man (the new and delete operators) and call the global operator new and operator delete directly? (Of course, if you're trying to instrument the new and delete operators, you actually ought to reimplement operator new and operator delete.)

void* my_alloc (size_t size)
{
   return ::operator new(size);
}

void my_free (void* ptr)
{
   ::operator delete(ptr);
}

Note that unlike malloc(), operator new throws std::bad_alloc on failure (or calls the new_handler if one is registered).

Ubiquitarian answered 4/6, 2009 at 7:34 Comment(1)
This is correct, as char does not have a constructor/destructor.Maramarabel
T
8

The question makes no sense. Your confusion may be partly due to the sloppy language people often use with delete:

You use delete to destroy an object that was dynamically allocated. Do do so, you form a delete expression with a pointer to that object. You never "delete a pointer". What you really do is "delete an object which is identified by its address".

Now we see why the question makes no sense: A void pointer isn't the "address of an object". It's just an address, without any semantics. It may have come from the address of an actual object, but that information is lost, because it was encoded in the type of the original pointer. The only way to restore an object pointer is to cast the void pointer back to an object pointer (which requires the author to know what the pointer means). void itself is an incomplete type and thus never the type of an object, and a void pointer can never be used to identify an object. (Objects are identified jointly by their type and their address.)

Tie answered 8/9, 2013 at 12:35 Comment(9)
Admittedly, the question doesn't make much sense without any surrounding context. Some C++ compilers will still happily compile such nonsensical code (if they're feeling helpful, might bark out a warning about it). So, the question was asked in order to assess the known risks of running legacy code that contains this ill-advised operation: will it crash? leak some or all of the character array memory? something else that's platform-specific?Blackamoor
Thanks for the thoughtful response. Upvoting!Blackamoor
@Andrew: I'm afraid the standard is pretty clear on this: "The value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject representing a base class of such an object. If not, the behavior is undefined." So if a compiler accepts your code without a diagnostic, it's nothing but a bug in the compiler...Tie
@KerrekSB - Re it's nothing but a bug in the compiler -- I disagree. The standard says the behavior is undefined. This means the compiler/implementation can do anything whatsoever and still be compliant with the standard. If the compiler's response is to say you can't delete a void* pointer, that's OK. If the compiler's response is to erase the hard drive, that's also OK. OTOH, if the compiler's response is to not generate any diagnostic but instead generate code that frees the memory associated with that pointer, that too is OK. This is a simple way to deal with this form of UB.Apricot
Just to add: I am not condoning the use of delete void_pointer. It's undefined behavior. Programmers should never invoke undefined behavior, even if the response appears to do what the programmer wanted done.Apricot
@DavidHammen: OK, fine. Think of "bug" as a "failure to be a good and responsible tool"...Tie
@DavidHammen: "If the compiler's response is to erase the hard drive, that's also OK". No, it is not. The behavior of the code being compiled is undefined, but the unlimited action arises when the code is executed (and furthermore, only if execution flow reaches that particular part of the code), not when it is compiled.Ngocnguyen
So, fill the pointed address memory of a void pointer with NULL make any difference for application memory management?Bosquet
"The question makes no sense" - yes, the question makes absolute sense.Sinistrous
S
6

Because char has no special destructor logic. THIS won't work.

class foo
{
   ~foo() { printf("huzza"); }
}

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}

The d'ctor won't get called.

Skutchan answered 2/6, 2009 at 21:7 Comment(0)
C
6

A lot of people have already commented saying that no, it's not safe to delete a void pointer. I agree with that, but I also wanted to add that if you're working with void pointers in order to allocate contiguous arrays or something similar, that you can do this with new so that you'll be able to use delete safely (with, ahem, a little of extra work). This is done by allocating a void pointer to the memory region (called an 'arena') and then supplying the pointer to the arena to new. See this section in the C++ FAQ. This is a common approach to implementing memory pools in C++.

Collbaith answered 2/6, 2009 at 21:14 Comment(0)
B
5

If you want to use void*, why don't you use just malloc/free? new/delete is more than just memory managing. Basically, new/delete calls a constructor/destructor and there are more things going on. If you just use built-in types (like char*) and delete them through void*, it would work but still it's not recommended. The bottom line is use malloc/free if you want to use void*. Otherwise, you can use template functions for your convenience.

template<typename T>
T* my_alloc (size_t size)
{
   return new T [size];
}

template<typename T>
void my_free (T* ptr)
{
   delete [] ptr;
}

int main(void)
{
    char* pChar = my_alloc<char>(10);
    my_free(pChar);
}
Balenciaga answered 2/6, 2009 at 21:33 Comment(3)
I didn't write the code in the example - stumbled upon this pattern being used in a couple places, curiously mixing C/C++ memory management, and was wondering what the specific dangers were.Blackamoor
Writing C/C++ is a recipe for failure. Whoever wrote that should have written one or the other.Glendoraglendower
@David That is C++, not C/C++. C does not have templates, nor does it use new and delete.Maramarabel
W
0

If you just want a buffer, use malloc/free. If you must use new/delete, consider a trivial wrapper class:

template<int size_ > struct size_buffer { 
  char data_[ size_]; 
  operator void*() { return (void*)&data_; }
};

typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer

OpaqueBuffer* ptr = new OpaqueBuffer();

delete ptr;
Worrywart answered 3/6, 2009 at 13:32 Comment(0)
W
0

There is hardly a reason to do this.

First of all, if you don't know the type of the data, and all you know is that it's void*, then you really should just be treating that data as a typeless blob of binary data (unsigned char*), and use malloc/free to deal with it. This is required sometimes for things like waveform data and the like, where you need to pass around void* pointers to C apis. That's fine.

If you do know the type of the data (ie it has a ctor/dtor), but for some reason you ended up with a void* pointer (for whatever reason you have) then you really should cast it back to the type you know it to be, and call delete on it.

Wigeon answered 7/5, 2013 at 1:25 Comment(0)
T
0

I have used void*, (aka unknown types) in my framework for while in code reflection and other feats of ambiguity, and so far, I have had no troubles (memory leak, access violations, etc.) from any compilers. Only warnings due to the operation being non-standard.

It perfectly makes sense to delete an unknown (void*). Just make sure the pointer follows these guidelines, or it may stop making sense:

1) The unknown pointer must not point to a type that has a trivial deconstructor, and so when casted as an unknown pointer it should NEVER BE DELETED. Only delete the unknown pointer AFTER casting it back into the ORIGINAL type.

2) Is the instance being referenced as an unknown pointer in stack bound or heap bound memory? If the unknown pointer references an instance on the stack, then it should NEVER BE DELETED!

3) Are you 100% positive the unknown pointer is a valid memory region? No, then it should NEVER BE DELTED!

In all, there is very little direct work that can be done using an unknown (void*) pointer type. However, indirectly, the void* is a great asset for C++ developers to rely on when data ambiguity is required.

Tarnish answered 4/1, 2014 at 23:17 Comment(0)
M
0

For the particular case of char.

char is an intrinsic type that does not have a special destructor. So the leaks arguments is a moot one.

sizeof(char) is usually one so there is no alignment argument either. In the case of rare platform where the sizeof(char) is not one, they allocate memory aligned enough for their char. So the alignment argument is also a moot one.

malloc/free would be faster on this case. But you forfeit std::bad_alloc and have to check the result of malloc. Calling the global new and delete operators might be better as it bypass the middle man.

Maramarabel answered 1/2, 2015 at 3:47 Comment(2)
"sizeof(char) is usually one" sizeof(char) is ALWAYS oneLevitt
Not until recently (2019) do people think that new actually is defined to throw. This is not true. It is compiler and compiler switch dependent. See for example MSVC2019 /GX[-] enable C++ EH (same as /EHsc) switches. Also on embedded systems many choose to not pay the performance taxes for C++ exceptions. So the sentence starting with "But you forfeit std::bad_alloc..." is questionable.Conventionalize

© 2022 - 2024 — McMap. All rights reserved.