Allocating a pointer by reference

I came across something that I don't understand well. Suppose I want to pass a character pointer to a function that references a void pointer.

void doStuff(void*& buffer)
{
  // do something
}

      

I would normally do something like this:

int main()
{
  unsigned char* buffer = 0;
  void* b = reinterpret_cast<void *>(buffer);
  doStuff(b);
  return 0;
}

      

Why is it not possible to directly pass the reinterpret_cast to the function?

int main()
{
  unsigned char* buffer = 0
  // This generate a compilation error.
  doStuff(reinterpret_cast<void *>(buffer));
  // This would be fine.
  doStuff(reinterpret_cast<void *&>(buffer));
  return 0;
}

      

There must be a good reason for this behavior, but I don't see it.

+3


source to share


4 answers


In the first example, you are actually passing the pointer variable b. This is how it works.

In the second example, the first reinterpret_cast returns a pointer (by value) that does not match the reference that the function should receive, and the second returns the specified reference.

As an example, to show you how links work, take a look at these two functions,

void doSomething( unsigned char *ptr );
void doSomethingRef( unsigned char *&ptr );

      

Let's say we have this pointer,

unsigned char *a;

      

Both functions are named the same,

doSomething( a ); // Passing pointer a by value
doSomethingRef( a );// Passing pointer a by reference

      

While it may seem like you are passing it by value, the function takes a reference, so it will be passed as a reference.



A reference is like a pointer, but it must be initialized with a left value and cannot be null.


Having said that, there are much better alternatives to using void * and especially void * &. void * makes the code harder to read and easier to kick yourself in the foot (if anything, forcing yourself to use these weird casts).

As I said in the comments, you can use the template and not worry about void casting.

template< class T > void doStuff( T *&buffer ) {
    ...
}

      

Or

template< class T > T* doStuff( T* buffer ) {
    ...
}

      

EDIT: On a side note, your second example is missing a semicolon,

unsigned char* buffer = 0; // Right here

      

+4


source


int main()
{
  unsigned char* buffer = 0;
  void* b = reinterpret_cast<void *>(buffer);
  doStuff(b);
  return 0;
}

      

b

is a pointer and doStuff(b)

gets the address of the pointer. Types match, b

has a type void*&

( *b

has a type void*

), and doStuff takes a type parameter void*&

.




int main()
{
  unsigned char* buffer = 0

  // This generate a compilation error.
  doStuff(reinterpret_cast<void *>(buffer));

  // This would be fine.
  doStuff(reinterpret_cast<void *&>(buffer));

  return 0;
}

      

The second call is similar to the call from the above function with b as a parameter.

The first call is just a pointer void

. The types are different, look closer void*

does not matchvoid*&

+1


source


This is how you would specify reinterpret_cast as a function argument directly, without using an intermediate variable. As others have told you, this is bad practice, but I want to answer your original question. This is of course for educational purposes only!

#include <iostream>

void doStuff(void*& buffer) {
    static const int count = 4;
    buffer = static_cast<void*>(static_cast<char*>(buffer) + count);
}

int main() {
    char str[] = "0123456789";
    char* ptr = str;
    std::cout << "Before: '" << ptr << "'\n";
    doStuff(*reinterpret_cast<void**>(&ptr));   // <== Here the Magic!
    std::cout << "After:  '" << ptr << "'\n";
}

      

Here we have a pointer to a char called ptr, and we want it to cast it to void * & (a void pointer reference) suitable to be passed as an argument to the doStuff function.

Although references are implemented as pointers, they are semantically more like transparent aliases for a different value, so the language does not provide the flexibility you get for manipulating pointers.

Trick: A dereferenced pointer is converted directly to the corresponding typed reference.

So, to get a reference to a pointer, we start with a pointer to a pointer:

&ptr  (char** - a pointer to a pointer to char)

      

Now the magic of reinterpret_cast brings us closer to our goal:

reinterpret_cast<void**>(&ptr)  (now void** - a pointer to a void pointer)

      

Finally, add the dereference operator and our masquerade is complete:

*reinterpret_cast<void**>(&ptr)   (void*& - a reference to a void pointer)

      

This compiles in Visual Studio 2013. This is what the program spits out:

Before: '0123456789'
After:  '456789'

      

The doStuff function has successfully advanced ptr by 4 characters, where ptr is a char *, passed by reference as a reinterpret_cast void *.

Obviously one of the reasons this demo works is because doStuff returns a pointer to char * in order to get the updated value. In real implementations, all pointers are the same size, so you may still be avoiding such manipulations when switching between types.

But if you start manipulating pointer values ​​with reinterpreted pointers, all kinds of evil can arise. You will probably also be breaking the "strict anti-aliasing" rule, so you can simply change your name to "Mr. Undefined Behavior" and join the circus. Freak.

+1


source


I'm not sure if this is correct, but ...

I consider this to be a simple comparison of the argument type:

void doStuff(void* buffer) {
    std::cout << reinterpret_cast<char*>(buffer) << std::endl;
    return;
}

      

You can do this and it int main()

will compile correctly.

A reference is different from a copy of a value β€” the difference is that the copied value does not have to reside in a variable or in a memory location β€” the copied value can be just a stack variable, while the reference does not need to point to an expiring value. This becomes important when you start playing around with referential and value semantics.

tl; dr: Don't mix references and meanings when casting. Performing operations on a reference is different from performing operations on a value; even if argument substitution is implicitly stripped.

0


source







All Articles