Could this memcpy result in undefined behavior?

2019-06-09 01:54发布

问题:

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.

回答1:

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

  1. 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.