Passing a void* by reference

10,400

Solution 1

If you take a void * by reference, you have to pass an actual void *, not an uint8_t *.

Try this instead:

template<typename T> inline void FreeAndNull(T * & V) { free(V); V = 0; }

EDIT: Modified sample to better reflect the OP's function name, and to address @6502's entirely correct comment.

Solution 2

The answer is yes, you can pass a void* by reference, and the error you're getting is unrelated to that. The problem is that if you have a function that takes void* by reference, then you can only pass in variables that actually are void*s as a parameter. There's a good reason for this. For example, suppose you have this function:

void MyFunction(void*& ptr) {
    ptr = malloc(137); // Get a block of raw memory
}

int* myIntArray;
MyFunction(myIntArray); // Error- this isn't legal!

The above code is illegal because of the indicated call, and for good reason. If we could pass in myIntArray into MyFunction, it would get reassigned to point to a buffer of type void* that isn't an int array. Consequently, on return from the function your int* would be pointing at an array of type void*, subverting the type system. This isn't to say that C++ has a strong type system - it doesn't - but if you're going to subvert it you have to explicitly put some casts in.

You similarly can't do this:

void MyFunction(void*& ptr) {
    ptr = malloc(137); // Get a block of raw memory
}

int* myIntArray;
MyFunction((void*)myIntArray); // Error- this isn't legal!

As a good reference as to why you can't do this, think about this code:

void OtherFunction(int& myInt) {
    myInt = 137;
}

double myDouble;
OtherFunction((int)myDouble); // Also error!

This isn't legal because if you tried passing in a double into a function that took an int&, then since int and double have fundamentally different binary representations, you'd end up clobbering the bits of the double with meaningless data. If this cast were to be legal, it would be possible to do Bad Things like this.

So in short, yes, you can take in a void* &, but if you do you have to pass in real void*s. As Erik pointed out above, if you want to free and zero a pointer templates are the way to go.

If you really do want to pass in a uint_8* into this your function, you could do so like this:

uint_8* uPtr = /* ... */
void* ptr = uPtr;
FreeAndNull(ptr);
uPtr = (uint_8*) ptr;

This requires explicit casting to tell the compiler "Yes, I know this may be unsafe, but I'm going to do it anyway."

Hope this helps!

Solution 3

Casting to void* doesn't work for a reason much simpler than what is being explained elsewhere: casting creates rvalues unless you explicitly specify a reference. You can't pass an rvalue off as a non-const reference.

Prove it? Try this:

void fun(void * &) {}
int main() { int * x; void * x2 = x; fun(x); }

If it's appropriate for you use, try this:

void fun(void * const&);

Now not only does casting work, its implicit.

Casting to reference can be dangerous. It does actually cause your code to compile but I won't speak to its behavior:

void fun(void *&){}
int main() { int * x; fun((void*&)x); }

My bet is that this will do very-bad-things(tm) since you're actually doing a reinterpret_cast here rather than a static cast.

Share:
10,400
Casebash
Author by

Casebash

Bachelor of Science (Adv Maths) with Honors in Computer Science from University of Sydney Programming C/C++/Java/Python/Objective C/C#/Javascript/PHP

Updated on June 14, 2022

Comments

  • Casebash
    Casebash almost 2 years

    Why can't I pass a void* by reference? The compiler allows me to declare a function with the following signature:

    static inline void FreeAndNull(void*& item)
    

    But when I try to call it, I get the following error:

    Error   1   error C2664: 'FreeAndNull' : cannot convert parameter 1 from 'uint8_t *' to 'void *&'
    

    Casting it to void* doesn't work either

    Also, are there any workarounds?