从我系统上的手册页:
void *memmove(void *dst, const void *src, size_t len);
描述
memmove() 函数将 len 个字节从字符串 src 复制到字符串 dst。
两个字符串可能重叠;复制始终以非破坏性
方式完成。
来自 C99 标准:
6.5.8.5 比较两个指针时,结果取决于所指向对象在地址空间中的相对位置。如果两个指向对象或不完整类型的指针都指向同一个对象,或者都指向同一个数组对象的最后一个元素之后,它们比较相等。如果指向的对象是同一个聚合对象的成员,则指向稍后声明的结构成员的指针比较大于指向结构中较早声明的成员的指针,并且指向具有较大下标值的数组元素的指针比较大于指向同一数组的元素的指针具有较低的下标值。所有指向同一个联合对象成员的指针比较相等。如果表达式
P
指向数组对象的一个元素,而表达式 Q 指向同一个数组对象的最后一个元素,则指针表达式Q+1
比较大于P
。在所有其他情况下,行为是 undefined。
重点是我的。
参数dst
和src
可以转换为指针以char
减轻严格的别名问题,但是是否可以比较两个可能指向不同块内的指针,以便在它们指向同一块内时以正确的顺序进行复制?
显而易见的解决方案是if (src < dst)
,但如果src
和dst
指向不同的块,那是未定义的。“未定义”意味着您甚至不应该假设条件返回 0 或 1(这在标准词汇中被称为“未指定”)。
另一种选择是if ((uintptr_t)src < (uintptr_t)dst)
,至少未指定,但我不确定标准是否保证src < dst
定义时,它等同于(uintptr_t)src < (uintptr_t)dst)
。指针比较是从指针算术定义的。例如,当我阅读关于加法的第 6.5.6 节时,在我看来,指针算术可能会朝着与uintptr_t
算术相反的方向发展,也就是说,兼容的编译器可能有, whenp
是 type char*
:
((uintptr_t)p)+1==((uintptr_t)(p-1)
这只是一个例子。一般来说,将指针转换为整数时似乎很少保证。
这是一个纯粹的学术问题,因为memmove
是与编译器一起提供的。在实践中,编译器作者可以简单地将未定义的指针比较提升为未指定的行为,或者使用相关的 pragma 来强制他们的编译器memmove
正确编译它们。例如,这个实现有这个片段:
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* As author/maintainer of libc, take advantage of the
* fact that we know memcpy copies forwards.
*/
return memcpy(dst, src, len);
}
我仍然想用这个例子来证明标准在未定义的行为方面走得太远,如果确实memmove
不能在标准 C 中有效地实现。例如,在回答这个 SO question时没有人打勾。