Reputation: 141
So I came across this piece of code reading about arduino's memory
void EEPROM_writeDouble(int address, double value)
{
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
{
EEPROM.write(address++, *p++);
}
}
What intrigues me is this line of code byte* p = (byte*)(void*)&value;
do completety but didn't figure it out or maybe. So we have a pointer of type byte(char)
that takes the reference of value of type void*
and convert it to an byte pointer?
Upvotes: 2
Views: 963
Reputation: 72311
Since a conversion from double*
to byte*
would not be a valid static_cast
or const_cast
, C++ interprets a C-style cast requesting that conversion as equivalent to a reinterpret_cast
. (I'm assuming byte
is a typedef for unsigned char
or char
.)
In the original C++98 Standard and in the C++03 Standard, there were essentially no guarantees on what a reinterpret_cast
will do, just saying that the result was "implementation-defined". So a single cast would probably get a pointer to the beginning of the storage for the double
variable, but technically you couldn't count on that.
On the other hand, the two conversions in the example, from double*
to void*
and then from void*
to byte*
, are valid static_cast
conversions, so the C-style casts would both have the behavior of a static_cast
. And a static_cast
to or from a (non-null) [cv] void*
has always guaranteed the void*
points at the beginning of the object's storage, resulting in a pointer to an imaginary byte
overlapping the storage of the double
object. (And reading from an unsigned char
or char
which is really in storage belonging to an object of a different type is a specific exception to the strict aliasing rules, so that's okay.)
Note that beginning with the C++11 Standard, the behavior of reinterpret_cast
, and therefore C-style casts, was more specified for certain categories of pointer conversions: ([expr.reinterpret.cast]/7)
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue
v
of type "pointer toT1
" is converted to the type "pointer to cvT2
", the result isstatic_cast<
cvT2*>(static_cast<
cvvoid*>(v))
if bothT1
andT2
are standard-layout types and the alignment requirements ofT2
are no stricter than those ofT1
, or if either type is void. Converting a prvalue of type "pointer toT1
" to the type "pointer toT2
" (whereT1
andT2
are object types and where the alignment requirements ofT2
are no stricter than those ofT1
) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
So using a recent Standard mode, the two casts are no longer necessary. (The code might have been written earlier, or might need to still work under multiple C++ versions, or might have just been written using old habits. Though using C++-style casts to say what sort of conversion logic you really mean is nearly always a better idea than using C-style casts.)
Upvotes: 3
Reputation: 141554
The standard defines that (byte *)p
is the same as (byte *)(void *)p
, if p
is a pointer.
In more detail, N4659 [expr.reinterpret.cast]/7 defines reinterpret_cast<T1 *>(p)
as static_cast<T1 *>(static_cast<void *>(p))
, and [expr.cast] covers that the two C-style casts resolve to static_cast
in this case.
So this cast is redundant, we could speculate that the author didn't know the language so well.
Upvotes: 2
Reputation: 51840
There's no actual reason for this cast. The code is doing type punning and the programmer who wrote the code was probably confused. When trying to get individual bytes out of a variable, you can use an unsigned char*
pointer. Which I assume is what byte
is in this example. You do not need to go through a void*
pointer for this.
Upvotes: 3