问题
uint32_t u32 = 0;
uint16_t u16[2];
static_assert(sizeof(u32) == sizeof(u16), "");
memcpy(u16, &u32, sizeof(u32)); // defined?
// if defined, how to we access the data from here on?
Is this defined behaviour? And, if so, what type of pointer may we use to access the target data after the memcpy
?
Must we use uint16_t*
, because that suitable for the declared type of u16
?
Or must we use uint32_t*
, because the type of the source data (the source data copied from by memcpy
) is uint_32
?
(Personally interested in C++11/C++14. But a discussion of related languages like C would be interesting also.)
回答1:
The C++ standard delegates to C standard:
The contents and meaning of the header
<cstring>
are the same as the C standard library header<string.h>
.
The C standard specifies:
7.24.1/3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).
So, to answer your question: Yes, the behaviour is defined.
Yes, uint16_t*
is appropriate because uint16_t
is the type of the object.
No, the type of the source doesn't matter.
C++ standard doesn't specify such thing as object without declared type or how it would behave. I interpret that to mean that the effective type is implementation defined for objects with no declared type.
Even in C, the source doesn't matter in this case. A more complete version of quote from C standard (draft, N1570) that you are concerned about, emphasis mine:
6.5/6 [...] If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. [...]
This rule doesn't apply, because objects in u16
do have a declared type
回答2:
Is this defined behavio[u]r?
Yes. memcpy
ing into a pod is well-defined and you ensured that the sizing is the correct.
Must we use
uint16_t*
, because that suitable for the declared type ofu16
?
Yes, of course. u16
is an array of two uint16_t
s so it must be accessed as such. Accessing it via a uint32_t*
would be undefined behavior by the strict-aliasing rule.
It doesn't matter what the source type was. What matters is that you have an object of type uint16_t[2]
.
On the other hand, this:
uint32_t p;
new (&p) uint16_t(42);
std::cout << p;
is undefined behavior, because now there is an object of a different type whose lifetime has begin at &p
and we're accessing it through the wrong type.
来源:https://stackoverflow.com/questions/39595103/memcpy-from-one-type-to-another-type-how-do-we-access-the-destination-afterward