【问题标题】:Copying structure in C with assignment instead of memcpy()用赋值而不是 memcpy() 在 C 中复制结构
【发布时间】:2012-10-28 08:29:36
【问题描述】:

直到最近,我只看到使用memcpy() 完成结构字段的复制。在课程和在线说明中,将一个结构的内容复制到另一个结构中通常看起来像

struct block *b0 = malloc(sizeof(struct block));
struct block *b1 = malloc(sizeof(struct block));
/* populate fields in *b0 */
memcpy(b1, b0, sizeof *b1); /* copy contents of b0 into b1 */
/* free b0, b1 */

但是,此任务也可以通过替换 memcpy() 的简单分配来完成。

*b1 = *b0; /* dereferenced struct assignment */

这是否有充分的理由不被广泛使用(至少在我有限的经验中)?这两种方法——assignment 和memcpy()——是等效的,还是有一些令人信服的理由来使用memcpy()

【问题讨论】:

  • 在 C 中这两种方法是等价的,它们都不做深拷贝。
  • memcpy(b1, b0, sizeof(struct block)); 的风格非常糟糕且容易出错。使用分配或memcpy(b1, b0, sizeof *b1);
  • @R..:你也可以支持memcpy(b1, b0, sizeof *b0)。使用sizeof * 可以消除两个可能的错误之一,而使用sizeof(struct block) 则两者都不会。
  • @Jeyaram 不久前忘记感谢您的编辑,谢谢。

标签: c pointers struct memcpy


【解决方案1】:

两种方法是等价的,都是浅拷贝。这意味着结构本身被复制,但结构引用的任何内容都不会被复制。

至于为什么memcpy更受欢迎,我不确定。旧版本的 C 不支持结构分配 (although it was a common extension as early as 1978),所以 memcpy 风格可能会成为制作更可移植代码的一种方式?无论如何,结构赋值在 PC 编译器中得到了广泛的支持,使用 memcpy 更容易出错(如果你弄错了大小,很可能会发生坏事),因此最好尽可能使用结构赋值。

但是,在某些情况下,只有 memcpy 有效。例如:

  • 如果您将结构复制到或从未对齐的缓冲区中复制 - 例如,保存/加载到/从磁盘或在网络上发送/接收 - 您需要使用 memcpy,因为结构分配需要源和目的地要正确对齐。
  • 如果您在一个结构之后打包附加信息,可能是using a zero-element array,您需要使用memcpy,并将这些附加信息纳入大小字段。
  • 如果要复制结构数组,可能执行单个 memcpy 比单独循环和复制结构更有效。再说一次,它可能不会。很难说,memcpy 实现的性能特征不同。
  • 某些嵌入式编译器可能不支持结构赋值。当然,有问题的编译器可能也不支持其他更重要的事情。

还要注意,虽然在 C 中 memcpy 和结构赋值通常是等价的,但在 C++ 中 memcpy 和结构赋值是等价的。一般来说,C++ 最好避免使用 memcpying 结构,因为结构分配可以而且经常会过载以执行其他操作,例如深拷贝或引用计数管理。

【讨论】:

  • 酷,谢谢。对深/浅复制混淆感到抱歉。我的印象是浅拷贝就像一个指针赋值,比如b1 = b0而不是*b1 = *b0
【解决方案2】:

这可能不是您要寻找的确切答案。

我正在解释我遇到的场景。

当我们使用memcpy() 时,它会逐字节复制到目的地。所以不用担心 ARM 架构中的数据对齐问题。如果你使用= 操作符,并且任何一个地址没有对齐到 4 字节,那么就会出现对齐错误。

来自 Arm 网站:

指向目标位置的指针,该位置比写入的最后一个字节高一个字节。这使得写入过程with perfect alignment of bytes 能够继续进行内存块的字符串连接。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0175k/Cihbbjge.html

【讨论】:

  • 编译器不应该把所有东西都正确对齐吗?
  • @alk,编译器假定一切都正确对齐,因为这可以在许多平台上显着提高性能。如果您只是使用 malloc 为您的结构分配内存,它将确保它是对齐的,但是如果您开始执行指针运算或从文件或网络中读取结构,则不再是这种情况,并且可能违反编译器的假设,导致未定义行为。请注意,您不会在 x86 上看到这一点,因为大多数 x86 操作透明地处理未对齐的访问,对性能有轻微影响。
  • @bdonlan 很公平,我看到了与从/向可能严重对齐的 I/O 缓冲区读取/写入相关的问题。无论如何,为什么使用(正确键入的)指针算法会出现问题,因为编译器应该知道(在编译时)生成正确代码所需的任何东西。
  • @alk 完全正确。只要你按规则行事,一切都是一致的。一旦你开始转换指向char * 的指针、添加偏移量并将它们转换回来,编译器就不再知道发生了什么。
  • "如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问此类对象或空间中此类对象的数组分配”——C99 §7.20.3 所以对齐在这里不是问题,memcpy 并不比结构分配好或差(除了更容易出错)。
【解决方案3】:

我重新提出这个老问题,因为答案没有解释为什么 memcpy 实际上是首选。

memcpy 是首选,因为它清楚地表明程序员想要复制内容而不仅仅是指针。

在下面的例子中,两个赋值做了两个非常不同的事情:

struct Type *s1,*s2;
*s1=*s2;
s1=s2;

不经意间使用其中一种代替另一种可能会产生灾难性的影响。编译器不会抱怨。除非程序在使用未初始化的指针时崩溃,否则错误可能会在很长一段时间内被忽视并产生奇怪的副作用。

把它写成以下之一:

memcpy(s1,s2,sizeof(*s1));
memcpy(s1,s2,sizeof(*s2));
memcpy(s1,s2,sizeof(struct Type));

让读者知道其目的是复制内容(以类型安全和边界检查为代价)。

一些编译器(例如 gcc)甚至在遇到类似以下情况时会发出有关 sizeof 的警告:

memcpy(s1,s2,sizeof(s1));

【讨论】:

  • 在最近的一次讨论中,一位同事坚持认为结构赋值更安全,因此更好。我喜欢你的回答,但不知道如何看待我同事的论点。我想双方都有优点。
【解决方案4】:

有些人更喜欢 memcpy,因为这是他们学到的东西,他们从来没有想过他们可以只做一个作业(在古代是不允许做作业的,但那是很久很久以前的事了)。由于 malloc () 分配的内存始终正确对齐,因此无需担心对齐问题。而且由于编译器可以简单地将这个赋值转换为 memcpy 调用,它永远不会比 memcpy 更慢或更多代码。当然,有些嵌入式系统的编译器已经过时了。

【讨论】:

    【解决方案5】:

    在嵌入式平台上工作的人会更喜欢使用 memcopy 而不是直接分配结构。 主要是在处理嵌入式平台时,有些编译器不支持直接结构赋值,需要用到memcopy。 如果你在电脑上工作,那么这两种情况都没有问题,两者都是有效的。

    【讨论】:

      猜你喜欢
      • 2011-07-19
      • 1970-01-01
      • 2013-06-18
      • 2014-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-24
      • 2011-04-13
      相关资源
      最近更新 更多