我正在 wikibooks.org 上查看公共领域的实现。它实现了 memmove(),如下所示,明确指出它"不完全可移植"!我想知道为什么:
- 括号放在代码的第一行,并且
- 该代码不是完全可移植的。
代码如下:
void *(memmove)(void *s1, const void *s2, size_t n)
{
char *p1 = s1;
const char *p2 = s2;
if (p2 < p1 && p1 < p2 + n) {
/* do a descending copy */
p2 += n;
p1 += n;
while (n-- != 0)
*--p1 = *--p2;
} else
while (n-- != 0)
*p1++ = *p2++;
return s1;
}
> 函数memmove()
的规范是它可以处理重叠的源和目标,但规范并没有说必须使用指向同一内存块(标准术语中的"对象")的指针来调用memmove()
。
当p1
和p2
是指向不同内存块的指针时,条件p2 < p1
是未定义的行为。C99标准说(6.5.8:5):
比较两个指针时,结果取决于相对 所指向对象的地址空间中的位置。如果两个 指向对象的指针或不完整的类型都指向同一对象, 或者两者都指向同一数组对象的最后一个元素,它们 比较相等。如果指向的对象是同一对象的成员 聚合对象,指向稍后声明的结构成员的指针比较 大于指向结构中较早声明的成员的指针, 和指向具有较大下标值的数组元素的指针比较 大于指向同一数组中具有较低元素的元素的指针 下标值。指向同一联合对象成员的所有指针 比较相等。如果表达式 P 指向数组的元素 对象和表达式 Q 指向相同的最后一个元素 数组对象,指针表达式 Q+1 比较大于 P。在 所有其他情况下,行为都是未定义的。
我不知道这是否是解释所指的,但它是不可移植性的明确来源之一。
不同的实现可能会使用 (uintptr_t)p2 < (uintptr_t)p1
。然后比较<
是整数之间的比较。转换为uintptr_t
会给出实现定义的结果。类型 uintptr_t
是在 C99 中引入的,它是一个无符号整数类型,保证保存指针的表示形式。
memmove()
的完全可移植实现可能使用第三个缓冲区来保存中间副本,或者使用==
比较(在必须使用它的上下文中给出指定的结果)。
- 括号
的解释在这里:函数名称周围的括号是什么意思?
由于
p2 < p1
和p1 < p2 + n
比较,它不便携。C 标准仅定义当两个指针指向同一对象时指针比较的行为。此代码取决于它们是否正常工作,即使您在不同对象之间复制也是如此。
从实际意义上讲,代码很好。当指针不指向同一对象时,复制是升序还是降序都无关紧要,因此比较的结果无关紧要。重要的是,代码不会做一些非常可怕的事情,比如使过程崩溃或发送核发射代码。 C 标准并不禁止这样做,但它不太可能在任何实际实现中。大多数实现只是比较原始地址,任何奇怪的实现只会返回一个不可预测的值,但没有任何副作用。