具有以下定义:
struct vector {
const float x;
const float y;
};
下面的代码段是否可能导致未定义的行为?
struct vector src = {.x=1.0, .y=1.0};
struct vector dst;
void *dstPtr = &dst;
memcpy(dstPtr, &src, sizeof dst);
gcc
和clang
不会发出任何警告,但会导致修改const限定类型。
该构造看起来很像How to initialize const members of structs on the heap 的公认答案中给出的构造,显然是一致的。我不明白我的示例将因此如何不符合要求。
答案 0 :(得分:2)
成员上的const
限定符使编译器假定-初始化对象后-不得以任何方式更改这些成员,并且可以相应地优化代码(例如,@Ajay Brahmakshatriya评论)。
因此,必须将 initialization 阶段与随后将应用 assignments 的阶段相区别,即,从何时开始,编译器可能会假定对象已初始化并具有一种有效的类型。
我认为您的示例与您引用的已接受答案之间存在主要区别。在this这样的答案中,具有常量限定成员类型的目标聚合对象是通过malloc
创建的:
ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);
根据有关如何访问对象的存储值的规则(参见an online c standard draft的这一部分),p
目标对象将在第一次访问时获得其有效类型。执行memcpy
的过程;那么有效类型就是源对象init
的类型,对象上第一个memcpy
的{{1}}可以看作是初始化。但是,之后修改目标对象的const成员将是UB(我认为即使第二个malloced
也会是UB,但这可能是基于意见的)。
6.5表达式
- 用于访问其存储值的对象的有效类型是该对象的声明类型(如果有的话)。87)... 如果值是 使用memcpy 或memmove复制到没有声明类型的对象中, 或复制为字符类型的数组,然后 修改后的对象,用于该访问以及随后的访问 不修改值是从哪个有效对象类型 该值将被复制(如果有)。对于所有其他访问 没有声明类型的对象,则该对象的有效类型为 只是用于访问的左值的类型。
87)分配的对象没有声明的类型。
但是,在您的示例中,目标对象memcpy
通过其定义dst
已经具有声明的类型。因此,struct vector dst;
成员上的const限定符在应用dst
之前就已经存在,并且必须被视为分配而不是初始化。
因此,在这种情况下,我将投票给UB。