With this definition:
struct vector {
const float x;
const float y;
};
would the code snippet below possibly result in undefined behavior?
struct vector src = {.x=1.0, .y=1.0};
struct vector dst;
void *dstPtr = &dst;
memcpy(dstPtr, &src, sizeof dst);
gcc
and clang
do not emit any warnings, but it does result in modification of a const-qualified type.
The construct looks a lot like the one given in the accepted answer to How to initialize const members of structs on the heap
, which apparently is conformant. I do not understand how my example would therefore be non-conformant.
The const
-qualifiers on members let the compiler assume that - after an object has been initialized - these members must not be altered through any way, and it may optimise code accordingly (cf, for example, @Ajay Brahmakshatriya comment).
So it is essential to distinguish the initialization phase from the subsequent phases where assignments would apply, i.e. from when on may a compiler assume that an object got initialized and has an effective type to rely on.
I think there is a main difference between your example and that in the accepted answer you cited. In this SO answer, the target aggregate object with const-qualified member types is created through malloc
:
ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);
According to the rules on how a stored value of an object may be accessed (cf. this part of an online c standard draft), the p
-target object will get its effective type the first time in the course of performing memcpy
; the effective type is then that of the source object init
, and the first memcpy
on an object that got malloced
can be seen as an initialization. Modifying the target object's const members afterwards, however, would be UB then (I think that even a second memcpy
would be UB, but that's probably opinion based).
6.5 Expressions
- The effective type of an object for an access to its stored value is the declared type of the object, if any.87) ... 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. For all other accesses to an
object having no declared type, the effective type of the object is
simply the type of the lvalue used for the access.
87) Allocated objects have no declared type.
In your example, however, the target object dst
already has a declared type through it's definition struct vector dst;
. Hence, the const-qualifiers on dst
's members are already in place before the memcpy
is applied, and it has to be seen as an assignment rather than an initialization.
So I'd vote for UB in this case.