【问题标题】:struct mystruct a = b; Is this valid? Direct assignment of structure variables, is it valid in C?结构 mystruct a = b;这是有效的吗?直接赋值结构变量,在C中有效吗?
【发布时间】:2012-07-11 11:29:59
【问题描述】:

我有以下代码。

struct rectangle {
int length;
int breadth;
};

int main (void) 
{
    struct rectangle a,b;
    a.length = 10;
    a.breadth = 5;

    b=a;  
    printf("\nValues of b.length = %d, b.breadth=%d\n",b.length,b.breadth);
    return 0;
}

上面的赋值语句是有效的吗?(b=a)我是在我的项目中这样做的。我收到了评论评论,说明这种类型的作业是错误的,我应该使用 memcpy。我打印了 b 的值并检查了。值是正确的。我想知道为什么上面的分配是错误的? 如果将结构变量传递给函数并将其捕获到单独的变量中时会发生什么错误?我希望我的问题很清楚。如果我对我的问题不清楚,请回复我。

【问题讨论】:

  • 您的代码确实无效,但这是由于main签名,而不是赋值。
  • 你能告诉我主签名有什么问题吗?
  • main 应该返回 int。 (除非:blablabala)
  • 有一个除非,用于“独立实现”。第二个签名应该是int main(void)
  • int main(void)!!! int main()不是签名。

标签: c coding-style


【解决方案1】:

在我看来,审稿人被误导了。非常正确,结构值和其他值一样,分配它们就可以了。而且它比调用 memcpy() 更清晰,而且更不容易出错。

当复制相同类型的值时,我总是不得不停下来思考将什么作为第三个参数传递给memcpy(),因为我发现哪个选择最清楚并不明显。所以,这是一个较少的教学问题,当然它读起来更好。

另外,编译器可能足够聪明复制任何可能潜伏在那里的额外填充和/或对齐字节,这是memcpy()永远做不到的。这是一个更高级别的构造,因此“更好”。

【讨论】:

  • 我刚和审稿人核实过。他提到,之所以给出审查意见,是因为如果结构内部有一个指针,这个过程会变得复杂。这在多大程度上有效?他指出的另一件事是,编译器正在优化赋值,但根据原始 C 标准和遵循这些标准的编译器,这种类型的赋值是错误的..
  • @happy2Help:我认为他有点困惑,因为他所说的没有任何意义:A)所以他同意赋值是有效的 C,但如果你想要它不起作用深拷贝。是的,但在这种情况下,两个 memcpy 都不够,因为赋值和 memcpy 在语义上是等价的。顺便说一句,你想要浅拷贝还是深拷贝?你首先有指针吗? B)您正在针对特定版本的语言编写代码。您是为 C89/C99 还是一些古老的 pre-ANSI C 编写?我认为答案是显而易见的,所以他的言论显然是愚蠢的。
  • 指针参数纯属伪造。这是关于语义的。并且在 OP 的示例中没有 is no 指针。我同意,有些地方需要“深拷贝*”,但这不是其中之一。
  • 我建议你的老板用更有能力的人代替那个人。
【解决方案2】:

这是,而且我认为一直都是有效代码,而您的评论评论错误

在我看来,评论评论很糟糕,因为评论者误导你养成坏习惯——当我们想要复制任意位的内存时,我们使用 memcpy——根据定义,结构定义良好,更干净,更容易阅读,而且你的方式是对的。

如果您将结构按值传递给函数,例如

struct a some_function(struct a)
{
    a.width = 99;
    return a;
}

传入的值是一个副本,返回值也是一个副本,这里唯一的危险是结构体很大,每次调用都会产生开销。

如果这不起作用,那么 C 世界的大部分将分崩离析......

在我看来,要让评论家们参与进来,他们正在更深入地挖掘自己的整体,应该承认自己错了。

这两种说法没有区别

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

如果编译器正在优化,那么它不应该破坏原始代码;否则这是编译器的一个错误——它是在提到原始的 C 标准(即 C89),因为我非常怀疑该标准是否允许错误的优化。

出于兴趣,我只是在标准 unix 环境中的 pre-ansi 编译器(从 1986 年开始)上尝试过这个。

/tmp> gcc a.c
a.c:
C-68000U 1.8.0 Copyright (c)1985,1986 Green Hills Software, Inc.
linking a:
/tmp> ./a
10, 5

为了进一步的兴趣,也许只有我的!我已经通过优化构建并查看了生成的代码。

结果表明,这个古老的、预标准的编译器按预期工作。 68k 的证明如下 - 我添加了带有 // 的 cmets 来解释发生了什么。

如您所见,优化的唯一结果是生成的代码在空间和执行时间方面都更加高效 - 除非您在嵌入式环境中工作,否则这并不重要

    ;       line  #13 memcpy(&b, &a, sizeof(b));
     140     PEA    0x8           // size
     144     PEA    (-8,FP)       // &a
     148     PEA    (-16,FP)      // &b
     152     JSR    memcpy        // call memcpy

    ;       line  #14
     170     LEA    (-8,FP),A1   // &a
     174     LEA    (-16,FP),A0  // &b
     178     MOVE.L (A1)+,(A0)+  // copy first element of struct
     180     MOVE.L (A1)+,(A0)+  // copy second element

对于更大的结构,为b=a 生成的代码是

     170     LEA    (-112,FP),A1
     174     LEA    (-224,FP),A0
     178     MOVEQ  #27,D0
     180     MOVE.L (A1)+,(A0)+
     182     DBF    D0,main+60

memcpy 版本的大小完全相同 - 但执行速度更快。

总之原审稿人错了,而且仍然是错的,应该承认他们的错误 - 除非他们指的是他们可以证明的具体案例(我对此表示怀疑)

【讨论】:

  • 使用 1986 编译器编译的事实并不意味着什么。
  • @akappa 是的,我记得这在 ANSI C89 之前一直有效,并且想检查一下。
  • @wildplasser void main() 错误也会是一个有效的评论评论......
  • 结构赋值一直是合法的。 VLA 在 C99 中可能是一个例外。 (就像 struct hack)
  • 请查看我提供的关于展开解决方案的评论
【解决方案3】:

您的同事可能正在考虑一个数组,其中赋值语句无效。

由于数组和结构都被分组为聚合,他可能认为同样的限制适用于结构类型的变量。

但是,请注意,这个赋值与 C 中的 memcpy 相同,但在 C++ 中不一定(对于 POD 类型,它仍然相同)。这可能是编码标准禁止它的充分理由,在这种情况下,您应该遵循贵公司的编码标准。

【讨论】:

    猜你喜欢
    • 2018-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    相关资源
    最近更新 更多