【问题标题】:C: does the assignment operator deep copy?C:赋值运算符是否深拷贝?
【发布时间】:2019-10-25 18:29:29
【问题描述】:

对于标量值,赋值运算符似乎将右侧的值复制到左侧。这对复合数据类型有何作用?例如,如果我有一个嵌套结构

struct inner {
    int b;
};

struct outer {
   struct inner a;
};

int main() {
   struct outer s1 = { .a = {.b=1}};   
   struct outer s2 = s1;
}
  • 赋值是否递归地深度复制值?
  • 将结构传递给函数时会发生同样的情况吗?

通过实验看起来确实如此,但是任何人都可以指出行为规范吗?

【问题讨论】:

  • 我不会把你正在做的事情称为“深拷贝”。我会将该术语保留为包含指向也需要复制的事物的指针的数据结构,而简单的赋值不会这样做。
  • 请注意,struct outer s2 = s1; 不是赋值,而是初始化。
  • @KamilCuk:正确。但是,在复制方面不等效吗?
  • @KamilCuk 这在 C 中并不完全相关,因为我们有 6.7.9 初始化§11:“与简单赋值相同的类型约束和转换适用”。 C++ 完全是另一回事。

标签: c struct assignment-operator


【解决方案1】:

没有“递归”;它复制值的所有(值)位。当然,指针不会被神奇地跟随,赋值运算符不会知道如何复制指向的数据。

你可以想到

a = b;

作为简写

memcpy(&a, &b, sizeof a);

sizeof 当然具有误导性,因为我们知道双方的类型相同,但我认为__typeof__ 没有帮助。

C11 规范草案说(在 6.5.16.1 简单分配,第 2 段):

在简单赋值(=)中,右操作数的值被转换为 赋值表达式的类型并替换存储在对象中的值 由左操作数指定。

【讨论】:

  • 术语递归通常在标准中正式使用,用于迭代结构成员,与递归函数没有任何关系。以 6.5 指针别名的规则为例:“- 在其成员中包含上述类型之一的聚合或联合类型(包括,递归,子聚合或包含联合的成员)”。还是struct初始化的规则:“如果是聚合,每个成员都按照这些规则进行初始化(递归),任何填充都初始化为零位;”
【解决方案2】:
  • 赋值是否递归地深度复制值?

    是的,就像您使用 memcpy 一样。指针被复制,但不是它们指向的内容。术语“深拷贝”通常意味着:同时复制指针指向的内容(例如在 C++ 复制构造函数中)。

    除了任何填充字节的值可能包含不确定的值。 (意味着结构上的memcmp 可能不安全。)

  • 将结构传递给函数时会发生同样的情况吗?

    是的。请参阅下面的 6.5.2.2 参考。

  • 通过实验看起来确实如此,但任何人都可以指出行为的规范吗?

    C17 6.5.16:

    赋值运算符将值存储在左操作数指定的对象中。一个 赋值表达式具有赋值后左操作数的值,但不是 一个左值。赋值表达式的类型是左操作数的类型 左值转换后。

    (在这种情况下,左值转换不相关,因为两个结构必须是 100% 相同且兼容的类型。简单地说:如果两个结构具有完全相同的成员,则它们是兼容的。)

    C17 6.5.16.1 简单赋值:

    • 左操作数具有结构或联合的原子、限定或非限定版本 类型与权利的类型兼容;

    C17 6.5.2.2 函数调用,§7:

    如果表示被调用函数的表达式具有包含原型的类型, 参数被隐式转换,就像通过赋值一样,...

【讨论】:

  • 两个结构或联合类型也必须具有相同的标签(或两者都没有标签)才能兼容。会员通信是不够的。实际上,在代码块内部,结构或联合类型仅与自身兼容。
  • @JohnBollinger 是的,但我不想将整个兼容的结构定义拖到这个答案中,因为它很复杂,因此“简单地说”。有兴趣的可以看看标准6.2.7章节下面的第一面文字墙。
  • 我明白你在做什么,我知道你已经知道结构兼容性的细节。这里没有必要详细介绍,但我认为重要的是传达成员通信是不够的,因为对于没有经验的人来说,这很容易造成误解。
  • @JohnBollinger 更糟糕的是,您可以拥有两个具有相同成员但标签或名称不同的结构。它们不是兼容的类型,但理论上您可以使用“联合通用初始序列黑客”通过指针通过左值访问它们的数据。这是一个晦涩的规则,导致了各种标准 DR 和编译器错误报告。 stackoverflow.com/a/54571011/584518
猜你喜欢
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多