【问题标题】:Using memcpy() to move tail of buffer to its beginning? (overlap)使用 memcpy() 将缓冲区的尾部移动到其开头? (重叠)
【发布时间】:2018-05-17 22:19:50
【问题描述】:

我有二进制文件读取缓冲区,它读取可变长度的结构。接近缓冲区的末尾总会有不完整的结构。我想将缓冲区的尾部移到它的开头,然后在下一个文件读取期间读取 buffer_size - tail_len 字节。像这样的:

char[8192] buf;
cur = 0, rcur = 0;
while(1){
  read("file", &buf[rcur], 8192-rcur);
  while (cur + sizeof(mystruct) < 8192){
    mystruct_ptr = &buf[cur];
    if (mystruct_prt->tailsize + cur >= 8192) break; //incomplete
    //do stuff
    cur += sizeof(mystruct) + mystruct_ptr->tailsize;
  }
  memcpy(buf,&buf[cur],8192-cur);
  rcur=8192-cur;
  cur = 0;
}

应该如果 tail 小而缓冲区大是可以的,因为 memcpy 很可能在单副本迭代期间不会与复制的内存段重叠。然而,当尾巴变大时听起来有点冒险 - 大于缓冲区的 50%。

如果缓冲区真的很大并且尾部也很大,那么它仍然应该没问题,因为在单个操作中可以复制多少数据存在物理限制,如果我没记错的话,对于使用矢量单元的现代 x86_64 CPU 来说是 512 字节。我考虑过添加检查尾部长度的条件,如果它与缓冲区的大小相比太大,则执行简单的逐字节复制,但问题是:

多大太大大到不能考虑这种重叠的memcpy 或多或少是安全的。 tail &gt; buffer size - 2kb?

【问题讨论】:

  • 为什么要冒险?如果源区域和目标区域有可能重叠,请使用memmove() 而不是memcpy()
  • &amp;buf[rcur] 也可以写成buf + rcur。您还需要实际检查来自read() 的返回值。

标签: c buffer memcpy


【解决方案1】:

根据标准,如果源区域和目标区域重叠,memcpy() 的行为未定义。区域有多大或有多少重叠并不重要。未定义的行为永远不能被认为是安全的。

如果您正在编写特定的实现,并且该实现定义了某些此类复制的行为,并且您不关心可移植性,那么您可以在这方面依赖您的实现的特定行为。但我建议不要。毕竟,这将是一个讨厌的错误,等待那些决定将代码与其他实现一起使用的人。甚至是未来的你。

在这种特殊情况下,如果选择使用专门用于此目的的memmove(),则使用memcpy() 赌博完全是鲁莽的。

【讨论】:

  • 我什至会说“忘记memcpy() 的存在——memmove() 总是正常工作,所以何必冒着不当行为的风险”。
猜你喜欢
  • 2014-10-27
  • 2010-12-29
  • 2022-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多