问题描述
具有以下定义:
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
和clang
不会发出任何警告,但会导致修改const限定类型.
gcc
and clang
do not emit any warnings, but it does result in modification of a const-qualified type.
构造看起来很像如何在堆上初始化const成员的答案中给出的构造,这显然是一致的.我不明白我的示例将因此如何不符合要求.
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.
推荐答案
成员上的const
限定符使编译器假定-在初始化对象之后-不得以任何方式更改这些成员,并且可以相应地优化代码(例如,参见@Ajay Brahmakshatriya评论).
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).
因此,必须将 initialization 阶段与随后应用 assignments 的各个阶段区分开,例如,从何时开始,编译器可能会假定对象已初始化并具有一种有效的类型.
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.
我认为您的示例与您引用的已接受答案之间存在主要区别.在此 SO答案中,具有常量限定成员类型的目标聚合对象是通过malloc
创建的:
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);
根据有关如何访问对象的存储值的规则(请参见在线c标准草稿),则p
目标对象将在执行memcpy
的过程中首次获得其有效类型;那么有效类型就是源对象init
的类型,并且获得malloced
的对象上的第一个memcpy
可以看作是初始化.但是,之后修改目标对象的const成员将是UB(我认为即使第二个memcpy
也会是UB,但这可能是基于意见的).
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).
- 访问其存储值的对象的有效类型是该对象的声明类型(如果有的话).87)... 如果值是 使用memcpy 或memmove复制到没有声明类型的对象中, 或复制为字符类型的数组,然后 修改后的对象,用于该访问以及随后的访问 不修改值是对象的有效类型,从中 该值将被复制(如果有的话).对于所有其他访问 没有声明类型的对象,则该对象的有效类型为 只是用于访问的左值的类型.
- 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)分配的对象没有声明的类型.
但是,在您的示例中,目标对象dst
通过其定义struct vector dst;
已经具有声明的类型.因此,dst
成员上的const限定符在应用memcpy
之前就已经存在,并且必须将其视为分配而不是初始化.
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.
因此,在这种情况下,我将投票给UB.
So I'd vote for UB in this case.
这篇关于此memcpy会导致未定义的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!