Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a void* by reference

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?

like image 439
Casebash Avatar asked Feb 16 '11 00:02

Casebash


People also ask

What does void * p mean?

which basically: declares a variable named p which is a pointer to type void (and pointers to void are compatible with pointers to any object type without requiring any explicit cast, including pointers to pointers to void — which is the type of &p );

What is void reference?

A reference to a void sufferes from the same problem, by definition the data pointed to doesn't have a type, therefore it can't be referenced in any meaningful way. To reference it you - the programmer - need to cast it to another type, then you can have a typed reference to it.

How do you pass-by-reference?

Pass by reference (also called pass by address) means to pass the reference of an argument in the calling function to the corresponding formal parameter of the called function so that a copy of the address of the actual parameter is made in memory, i.e. the caller and the callee use the same variable for the parameter.

What does pass-by-reference mean in C?

Passing by reference literally just means passing the memory address of where a variable is stored rather than the variable's value itself. That is what C allows, and it is pass-by-reference every time you pass a pointer, because a pointer is a reference to a variables memory location.


3 Answers

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!

like image 179
templatetypedef Avatar answered Oct 13 '22 11:10

templatetypedef


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.

like image 22
Erik Avatar answered Oct 13 '22 10:10

Erik


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.

like image 25
Edward Strange Avatar answered Oct 13 '22 10:10

Edward Strange