【问题标题】:Is it legal to use memcpy with a destination structure with constant members?将 memcpy 与具有常量成员的目标结构一起使用是否合法?
【发布时间】:2017-06-30 02:07:36
【问题描述】:

例如,下面的函数是否合法:

struct two_int {
  const int a, b;
}

void copy_two(const two_int *src, two_int *dest) {
  memcpy(dest, src, sizeof(two_int));
}

似乎至少某些类型的常量定义值的修改是不允许的,但我不清楚这是否符合条件。

如果答案是“一般来说不允许”,我还想知道dest 是用malloc 新分配的内存的特殊情况(因此没有' t 尚未被赋予任何值),例如:

two_int  s = {.a = 1, .b = 2};
two_int *d = malloc(sizeof(two_int));
copy_two(&s, d);

更新:对于新的malloc'd 结构的情况,后一个问题似乎得到了肯定的回答(没关系),但原来的、更一般的问题仍然存在,我想。

【问题讨论】:

  • 没有。试图在任何地方修改常量变量是未定义的行为
  • 这意味着two_int 不能永远通过malloc 或以其他方式动态分配,对吧?由于您无法将任何施工信息传递给malloc...
  • 这个问题可能符合 [language-lawyer] 标签的条件。
  • 不幸的是,链接的问题只涵盖了新的mallocd 结构的特殊情况,所以它没有回答我最初的问题,即是否允许在它们之上进行memcpy 一般.

标签: c struct initialization constants


【解决方案1】:

仅当实际目标对象没有静态或自动持续时间时,将memcpy 用于此类目的才会定义行为。

给定代码:

struct foo { double const x; };
void outsideFunction(struct foo *p);

double test(void)
{
  struct foo s1 = {0.12345678};
  outsideFunction(&s1);
  return 0.12345678;
}

编译器有权将函数优化为:

double test(void)
{
  static struct foo const s1 = {0.12345678};
  outsideFunction(&s1);
  return s1.x;
}

在许多处理器上,使用任意常量加载双精度的唯一方法是从持有该常量的对象中加载其值,在这种情况下,编译器会方便地知道必须持有该常量的对象 (s1.x) 0.12345678。最终结果是使用memcpy 写入s1.x 的代码可能会破坏数字常量0.12345678 的其他用途。俗话说,“变量不会,常量不会”。讨厌的东西。

对于已分配持续时间的对象不会存在该问题,因为 memcpy 要求编译器“忘记”关于目标存储中先前存储的所有内容,包括任何“有效类型”。静态和自动对象的声明类型独立于存储在其中的任何内容而存在,并且不能通过“memcpy”或任何其他方式擦除,但分配持续时间对象只有一个“有效类型”会被擦除。

【讨论】:

  • 这是一个有效的例子,但这不是一般问题的特例吗?如果排除仅由 const 成员组成的结构,那么它可以复制吗?
  • @jxh:我从一个更复杂的示例开始,在这种情况下会失败。假设struct foo 有非常量成员,但该函数接受一个参数v,将s1.x 初始化为v+0.12345678,然后返回v+0.12345678。编译器将有权注意到s1.x 中的值与它需要返回的值精确匹配,从而加载该值。我可以看到拥有更强大的const 形式的用处,这将保证如果代码读取对象的值,则以后会使用派生的指针*读取该对象...
  • ...从该对象的地址没有任何类型转换*会产生相同的值,但这需要认识到类型转换应该影响编译器对指针目标的假设。我认为很明显他们应该这样做,但是像 gcc 这样的编译器的设计会使这变得困难。
  • 我误解了你的例子。您通过允许复制来展示意外行为。但是,mallocd 结构的memcpy 可以表现出相同的意外行为。 struct foo *s = memcpy(malloc(sizeof(*s)), &foo_init, sizeof(*s)); return s;s 返回时,可能会有类似的代码:if (s->x == SOME_CONSTANT) { memcpy(s, &foo_next, sizeof(*s)); return SOME_CONSTANT; }。如果编译器注意到s->x 应该是const,并将其优化为return s->x,那么同样的坏事就会发生。
  • @jxh:第一次赋值只会设置有效类型,直到下一次代码尝试写入存储。 memcpy 将擦除有效类型,执行复制,然后设置有效类型以匹配源的类型。对于某些应用领域,制定使有效类型适用于底层存储的生命周期的规则会有所帮助,但委员会故意避免这样做。恕我直言,应该有一种方法可以让代码放弃更改有效类型的能力,从而允许更多优化,如果代码不会......
【解决方案2】:

首先请注意,从广义上讲,几乎任何事情都是允许的:它可能只是具有未定义的行为。您可以将任意数字reinterpret_cast<> 作为地址,然后写入该地址——完全“合法”的 C++。至于它做什么 - 通常,不能保证。这里是龙。

但是,如对此问题的回答所示:

behavior of const_cast in C++

如果你指向的结构最初没有被定义为const,并且只是在你的代码的路上被const'ified,那么const_cast'ing 引用或指向它的指针并设置指向-如您所料,to values 是成功所必需的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 2020-03-06
    • 1970-01-01
    相关资源
    最近更新 更多