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.
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: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 ofvoid*
. That will avoid any aliasing issues entirely, since the correct type is always used.This is UB as you are assigning a
double*
variable as if it was avoid*
variable (It has the same effect asreinterpret_cast<void*&>(buffer) = malloc(...);
in C++. This would be OK on most systems asvoid*
anddouble*
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: