原因
各种各样的标准库函数在将字节序列从一个存储区复制到另一个存储区的作用之中。 当源区域和目标区域重叠时,这些功能大多数都具有不确定的行为。
#include <string.h> /* for memcpy() */
char str[19] = "This is an example";
memcpy(str + 7, str, 10);
尝试复制10个字节,其中源存储区和目标存储区重叠三个字节。 可视化:
overlapping area
|
_ _
| |
v v
T h i s i s a n e x a m p l e \0
^ ^
| |
| destination
|
source
由于存在重叠,因此导致的行为是不确定的。
在具有此类限制的标准库函数中,有memcpy(),strcpy(),strcat(),sprintf()和sscanf()。 该标准说明了这些功能以及其他几个功能:
如果在重叠的对象之间进行复制,则行为是不确定的。
解决方法
memmove()函数是该规则的主要例外。 它的定义指定该函数的行为就像首先将源数据复制到一个临时缓冲区中,然后将其写入目标地址一样。 重叠的源区域和目标区域也不例外,也不需要一个区域,因此memmove()在这种情况下具有明确定义的行为。
这种区别反映了效率与一般性的权衡。 诸如这些功能执行的复制通常发生在不相交的内存区域之间,并且经常有可能在开发时知道特定的内存复制实例是否属于该类别。 假设非重叠提供了相对更有效的实现,当假设不成立时,这些实现不会可靠地产生正确的结果。 大多数C库函数都允许更有效的实现,并且memmove()填补了空白,可用于源和目标可能重叠或确实重叠的情况。 为了在所有情况下都能产生正确的效果,它必须执行其他测试和/或采用效率相对较低的实现。