Accessing pointer variable as a pointer to a diffe

2019-07-27 03:41发布

问题:

Is it good practice to access a pointer variable by dereferencing a pointer to a pointer, which points to a different type or void? Could this break strict aliasing rules? C and C++ have some differences in aliasing rules. In this question we focus on C++. The other question considering C can be found here. In the following example a double* is accessed as a void*.

int create_buffer(void** ptr, ...)
{
    *ptr = malloc(...);
    ...
}

int main(void)
{
    double* buffer;

    // The problematic code is here, double**
    // is coerced to void**, which is later
    // dereferenced by the function
    create_buffer(reinterpret_cast<void**>(&buffer), ...);
    ...
}

If this is causes UB, what about the following?

// process A
int* p;  ...
printf("%p", p); // UB?

// process B
int* p;
scanf("%p", &p); // UB?

This looks like a bad example, but what if two processes talk to each other through pipes, and eventually one process passes a pointer to globally allocated memory to the other process.

回答1:

Is it good practice to access...

No. void* is not the go to type for polymorphism and reuse in C++, even without considering the aliasing issues in your original code. With a rich template mechanism available you can make your code strongly typed and safer to boot. The obvious improvement is to use templates, and type safe allocation:

template<typename T>
int create_buffer(T** ptr, ...)
{
    *ptr = new T[...];
    ...
}

But to go off on a tangent, this is still not how good C++ will look like. Good C++ is about managing complexity correctly. And tracking a buffer is a complex task. The good C++ approach is to not do it by hand, but to encapsulate it in a dedicated class (template). In fact, this is such a common task, that the standard library provides a solution.

The good practice is to use std::vector<double> instead of the buffer creation function. A class template for a type generic task will often beat any use of void*. That will avoid any aliasing issues entirely, since the correct type is always used.



回答2:

This is UB as you are assigning a double* variable as if it was a void* variable (It has the same effect as reinterpret_cast<void*&>(buffer) = malloc(...); in C++. This would be OK on most systems as void* and double* are usually exactly the same, but it is still UB, so might not work on all implementations).

A solution would be to assign to a different variable and then reassign it:

int main(void)
{
    double* buffer;

    {
        void* result;
        create_buffer(&result, ...);
        buffer = (double*) result;  // Cast needed for C++
    }
    ...
}