C 作业操作员是否进行深度复制?

C 作业操作员是否进行深度复制?,c,struct,assignment-operator,C,Struct,Assignment Operator,对于标量值,赋值运算符似乎将右侧的值复制到左侧。对于复合数据类型,这是如何工作的?例如,如果我有一个嵌套结构 结构内部{ int b; }; 结构外部{ 结构内部a; }; int main(){ 结构外部s1={.a={.b=1}; 结构外部s2=s1; } 赋值是否递归地深度复制值 将结构传递给函数时是否也会发生同样的情况 通过实验,它看起来是这样的,但是有人能指出行为的规范吗?没有“递归”;它复制值的所有(值)位。指针不会被神奇地跟随。当然,赋值运算符不知道如何复制指向的数据 你可以

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

结构内部{ int b; }; 结构外部{ 结构内部a; }; int main(){ 结构外部s1={.a={.b=1}; 结构外部s2=s1; }
  • 赋值是否递归地深度复制值
  • 将结构传递给函数时是否也会发生同样的情况
通过实验,它看起来是这样的,但是有人能指出行为的规范吗?

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

你可以想到

a = b;
简写

memcpy(&a, &b, sizeof a);
当然,
sizeof
是误导性的,因为我们知道两边的类型是相同的,但我认为
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

C11规范草案说明(在6.5.16.1简单赋值第2段中):

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

  • 赋值是否递归地深度复制值

    是的,就像您会使用
    memcpy
    一样。指针是复制的,但不是它们所指向的。术语“深度副本”通常意味着:也复制指针指向的指针(例如在C++复制构造函数中)。 除此之外,任何填充字节的值都可能包含不确定的值。(这意味着结构上的
    memcmp
    可能不安全。)

  • 将结构传递给函数时是否也会发生同样的情况

    对。见下文6.5.2.2的参考

  • 通过实验,它看起来是这样的,但是有人能指出行为的规范吗

    C17 6.5.16:

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

    (本例中的左值转换不相关,因为两个结构必须是100%相同且兼容的类型。简单地说:如果两个结构的成员完全相同,则它们是兼容的。)

    C17 6.5.16.1简单赋值:

    • 左操作数具有结构或并集的原子、限定或非限定版本 与权利类型兼容的类型
    C17 6.5.2.2函数调用,§7:

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


术语递归通常在标准中正式使用,用于迭代结构成员,而与递归函数无关。以6.5指针别名规则为例:“-在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的成员)”。或者结构初始化规则:“如果它是一个聚合,则根据这些规则(递归地)初始化每个成员,并且任何填充都初始化为零位;”我不会将您正在执行的操作称为“深度复制”。我会为包含指向同样需要复制的对象的指针的数据结构保留该术语,而简单的赋值不能做到这一点不是赋值,而是初始化。@KamilCuk:正确。但是,在复制方面不是等价的吗?@KamilCuk这在C中并不完全相关,因为我们有6.7.9初始化§11:“与简单赋值相同的类型约束和转换适用”。C++是另一个完全相同的故事。两个结构或联合类型必须具有相同的标签(或者两者都不加标签)以兼容。成员通信是不够的。实际上,在代码块内部,结构或联合类型只与自身兼容。@JohnBollinger是的,但我不想将整个兼容结构定义拖到这个答案中,因为它很复杂,因此“简单地说”。对于那些感兴趣的人来说,这是标准中第6.2.7章下面的第一道墙。我理解你在做什么,我知道你已经知道结构兼容性的细节。没有必要在这里详细说明,但我认为重要的是要传达,成员通信是不够的,因为对于没有经验的人来说,这很容易造成误解。@JohnBollinger更糟糕的是,你可以有两个结构,其成员相同,但标签或名称不同。它们是不兼容的类型,但理论上,您可以使用“联合公共初始序列hack”通过指针访问它们的数据。这是一条模糊的规则,导致了各种标准DR和编译器错误报告。