我在查看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;
}
答案 0 :(得分:9)
函数memmove()
的规范是它可以处理重叠的源和目标,但规范并没有说必须使用指向同一内存块(“对象”)的指针调用memmove()
标准的说法。
当p1
和p2
是指向不同内存块的指针时,条件p2 < p1
是未定义的行为。 C99标准说(6.5.8:5):
当比较两个指针时,结果取决于相对值 指向的对象的地址空间中的位置。如果两个 指向对象或不完整类型的指针都指向同一个对象, 或者两者都指向同一个数组对象的最后一个元素,它们 比较平等。如果指向的对象是同一个成员 聚合对象,指向稍后声明的结构成员的指针 大于指向结构中先前声明的成员的指针, 和指向具有较大下标值的数组元素的指针进行比较 大于指向同一数组元素的指针 下标值。指向同一个union对象的成员的所有指针 比较平等。如果表达式P指向数组的元素 对象和表达式Q指向同一个的最后一个元素 数组对象,指针表达式Q + 1比较大于P. 在所有其他情况下,行为未定义。
我不知道这是否是解释所指的内容,但它是不可移植性的明确来源。
其他实现可能会使用(uintptr_t)p2 < (uintptr_t)p1
。然后比较<
是整数之间的比较。转换为uintptr_t
可提供实现定义的结果。类型uintptr_t
是在C99中引入的,是一种无符号整数类型,保证保存指针的表示。
memmove()
的完全可移植实现可能使用第三个缓冲区来保存中间副本,或use ==
comparison(在必须使用它的上下文中给出指定结果)。
答案 1 :(得分:5)
括号的解释如下:What do the parentheses around a function name mean?
由于p2 < p1
和p1 < p2 + n
比较,它无法移植。当两个指针指向同一个对象时,C标准仅定义指针比较的行为。即使您在不同对象之间进行复制,此代码依赖于它们合理地工作。
实际上,代码很好。当指针不指向同一个对象时,复制是上升还是下降并不重要,因此比较结果无关紧要。重要的是代码不会做一些非常可怕的事情,比如崩溃过程或发送核启动代码。 C标准并没有禁止这一点,但它不可能在任何实际的实现中。大多数实现只是比较原始地址,任何奇怪的实现只会返回一个不可预测的值,但没有任何副作用。