【问题标题】:Could this memcpy result in undefined behavior?这个 memcpy 会导致未定义的行为吗?
【发布时间】:2019-01-17 20:43:07
【问题描述】:

有了这个定义:

struct vector {
    const float x;
    const float y;
};

下面的代码 sn-p 是否会导致未定义的行为?

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 的已接受答案中给出的构造,显然是一致的。我不明白我的例子怎么会不符合标准。

【问题讨论】:

  • 查看OP的最后一个问题:stackoverflow.com/questions/54242966/…
  • 具有const 成员的struct不能assignment设置,只能由initialization设置(你为src 做的)。 memcpy 类似于赋值。从struct 中删除const,一切都会好起来的。 IMO,结构成员上的const 并不是那么有用,因为您遇到了这个问题。请注意,即使使用 constmemcpy 也不会造成任何伤害,但这是不好的做法。
  • @CraigEstey 它可能会造成伤害。考虑代码 - {struct vector foo={...}; float a = foo.x ;memcpy(&foo, ..., sizeof(foo)); int b = foo.x;}。编译器可以重用a的值来设置b
  • 似乎是个好问题,不知道为什么有人反对它(尽管最后一段是不必要的)
  • 我不明白为什么这个问题被否决了;这是一个合理且表述清楚的问题,我认为这不是直截了当的答案。

标签: c struct constants language-lawyer


【解决方案1】:

成员上的const-qualifiers 让编译器假设 - 在对象被初始化后 - 这些成员不得以任何方式更改,并且可以相应地优化代码(例如,参见 @Ajay Brahmakshatriya 评论)。

因此,必须将 初始化 阶段与 assignments 应用的后续阶段区分开来,即从何时开始,编译器可能会假设对象已初始化并具有一种有效的依赖类型。

我认为您的示例与您引用的公认答案之间存在主要区别。在this SO 答案中,通过malloc 创建具有const 限定成员类型的目标聚合对象:

ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);

根据如何访问对象的存储值的规则(参见an online c standard draft的这一部分),p-target对象将在执行@的过程中第一次获得其有效类型987654328@;有效类型是源对象init 的类型,获得malloced 的对象上的第一个memcpy 可以看作是初始化。然而,之后修改目标对象的 const 成员将是 UB(我认为即使是第二个 memcpy 也会是 UB,但这可能是基于意见的)。

6.5 表达式

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

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

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

所以在这种情况下我会投票给 UB。

【讨论】:

  • 感谢您的回复,这是一个有趣的引用,似乎确实可以解释它。但是,我在解释“没有声明类型的对象”时遇到了问题。 p 不是声明为 ImmutablePoint * 吗?
  • 我不这么认为; p 与它所指向的对象不同; p 具有声明的类型 ImmutablePoint *,但它指向的对象甚至还没有创建。
  • 好的,我并不完全清楚在这种情况下“对象”是什么意思。我还在7.22.3.4 中找到“malloc 函数为一个 object 分配空间,该对象的大小由 size 指定,其值是不确定的。”,我想它的类型也是不确定的。跨度>
  • 我还想在这段代码中补充一点——foo(const char* t) {char s = *t; some_function_call(t); char r = *t;},即使t 被标记为指向const 限定的char,编译器也无法优化第二次读取.这是因为t 最初可能不指向const 对象,而some_function_call 修改内容是完全有效的。但这种情况很特殊,因为const 在结构内部。任何指针的字段总是const
  • malloc 分配的@ReinierTorenbeek 对象还没有类型。它们可以通过写入对象来设置有效类型
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-11
  • 2016-09-04
  • 1970-01-01
  • 2013-04-24
  • 1970-01-01
  • 2016-02-17
  • 2022-11-16
相关资源
最近更新 更多