此memcpy会导致未定义的行为吗?

时间:2019-01-17 20:43:08

标签: c struct const language-lawyer

具有以下定义:

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);

gccclang不会发出任何警告,但会导致修改const限定类型。

该构造看起来很像How to initialize const members of structs on the heap 的公认答案中给出的构造,显然是一致的。我不明白我的示例将因此如何不符合要求。

1 个答案:

答案 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表达式

     
      
  1. 用于访问其存储值的对象的有效类型是该对象的声明类型(如果有的话)。87)... 如果值是   使用memcpy 或memmove复制到没有声明类型的对象中,   或复制为字符类型的数组,然后   修改后的对象,用于该访问以及随后的访问   不修改值是从哪个有效对象类型   该值将被复制(如果有)。对于所有其他访问   没有声明类型的对象,则该对象的有效类型为   只是用于访问的左值的类型。
  2.   
     

87)分配的对象没有声明的类型。

但是,在您的示例中,目标对象memcpy通过其定义dst已经具有声明的类型。因此,struct vector dst;成员上的const限定符在应用dst之前就已经存在,并且必须被视为分配而不是初始化。

因此,在这种情况下,我将投票给UB。