使用 memcpy() 将缓冲区的尾部移动到其开头? (交叠)

Using memcpy() to move tail of buffer to its beginning? (overlap)

我有读取可变长度结构的二进制文件读取缓冲区。在缓冲区末尾附近总会有不完整的结构。我想将这样的缓冲区尾部移动到它的开头,然后在下一次读取文件时读取 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%。

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

如何太大 考虑这种重叠 memcpy 或多或少是安全的。 tail > buffer size - 2kb?

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

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

在这种特殊情况下,可以选择使用专用于此目的的 memmove(),这使得使用 memcpy() 进行赌博变得非常鲁莽。