1

这是一个出于好奇而不是任何重要的问题,但我只是想知道memmove 文档中的以下代码段:

复制就像使用了中间缓冲区一样发生

(强调我的)。该公式向我表明,是否使用中间缓冲区是特定于编译器实现的。

如果您要我写memmove,我可能会自动执行以下操作:

  • n在堆上分配字节
  • memcpy温度的来源
  • memcpy到目的地的温度
  • 释放缓冲区

我希望任何人都可以...

  1. ...确认该公式是否只是因为用户更容易可视化正在发生的事情,而没有解决特定实现实际上必须使用中间缓冲区的问题;
  2. ...阐明了一些常见 C++ 编译器(如或 Visual C++)中的实际gcc实现- 例如,是否使用缓冲区并检查重叠是否可以memcpy直接执行;
  3. ...也许指出我上面的简单算法中明显的错误/效率低下。
4

4 回答 4

5
  1. 的确。“好像”意味着它必须表现得像它所做的那样;但并不限制实现实际做到这一点。唯一需要的行为是目标缓冲区以来自源缓冲区的正确字节结束,无论缓冲区是否重叠。

  2. 一个常见的实现是,如果目标在源之前开始,则从缓冲区的开头向前复制字节,否则从结尾向后复制。这确保了源字节在被覆盖之前总是被读取,如果有重叠的话。

  3. 没有错误,除非分配失败。效率低下是分配和释放临时缓冲区,并将每个字节复制两次而不是一次。

于 2014-04-07T14:20:18.143 回答
3

你在#1 上是绝对正确的——描述是为了帮助用户在逻辑上可视化正在发生的事情,而不是解释它是如何实现的。

但是,没有一个理智的实现实际上会使用昂贵的临时缓冲区来完成它,因为避免重复复制所需要做的就是决定是从头复制还是从尾复制。这是一个可以做到这一点的示例实现。

您的算法的唯一问题是它可能会在不需要时使系统内存不足:想象您的程序试图移动一个大小为允许的总内存的 60% 的缓冲区,以查看何时会发生的示例。

于 2014-04-07T14:20:03.217 回答
2

如果缓冲区不重叠,首先想到的优化机会之一是做一个普通的 memcpy() 。由于(虚拟)地址空间的平坦性质,很容易检查。我查看了glibcAndroid的实现,两者都这样做(Android 的初学者更容易理解)。

在堆上分配内存可能是不行的,因为它会很慢(动态分配并不便宜)。

如果缓冲区确实重叠,我们可以优化不重叠部分的复制,对于其余部分,我们可能会使用一个小的暂存缓冲区,但如果确实需要任何分配,那将是堆栈分配的。Android 一次只复制一个字节;我们可以在 amd64 上做得更好,但这与 memcpy 中已经完成的优化相同。glibc 根据重叠的性质向前或向后复制(这就是源中的“BWD”所指的内容)。

于 2014-04-07T14:21:43.133 回答
1

实施无关紧要。措辞是为了保证正确处理内存。

char buf[] = { 0x11, 0x22, 0x33, 0x00 };
memcpy(buf, buf + 1, 3);

可能导致buf.{ 0x11, 0x11, 0x11, 0x11 }

然而

char buf[] = { 0x11, 0x22, 0x33, 0x00 };
memmove(buf, buf + 1, 3);

保证buf{ 0x11, 0x11, 0x22, 0x33 }

于 2014-04-07T14:25:10.577 回答