C语言 关于内存移动的实施



我正在 wikibooks.org 上查看公共领域的实现。它实现了 memmove(),如下所示,明确指出它"不完全可移植"!我想知道为什么:

  1. 括号放在代码的第一行,并且
  2. 该代码不是完全可移植的。

代码如下:

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()

p1p2是指向不同内存块的指针时,条件p2 < p1未定义的行为。C99标准说(6.5.8:5):

比较两个指针时,结果取决于相对 所指向对象的地址空间中的位置。如果两个 指向对象的指针或不完整的类型都指向同一对象, 或者两者都指向同一数组对象的最后一个元素,它们 比较相等。如果指向的对象是同一对象的成员 聚合对象,指向稍后声明的结构成员的指针比较 大于指向结构中较早声明的成员的指针, 和指向具有较大下标值的数组元素的指针比较 大于指向同一数组中具有较低元素的元素的指针 下标值。指向同一联合对象成员的所有指针 比较相等。如果表达式 P 指向数组的元素 对象和表达式 Q 指向相同的最后一个元素 数组对象,指针表达式 Q+1 比较大于 P。在 所有其他情况下,行为都是未定义的。

我不知道这是否是解释所指的,但它是不可移植性的明确来源之一。

不同的实现可能会使用 (uintptr_t)p2 < (uintptr_t)p1 。然后比较<是整数之间的比较。转换为uintptr_t会给出实现定义的结果。类型 uintptr_t 是在 C99 中引入的,它是一个无符号整数类型,保证保存指针的表示形式。

memmove()的完全可移植实现可能使用第三个缓冲区来保存中间副本,或者使用==比较(在必须使用它的上下文中给出指定的结果)。

    括号
  1. 的解释在这里:函数名称周围的括号是什么意思?

  2. 由于p2 < p1p1 < p2 + n比较,它不便携。C 标准仅定义当两个指针指向同一对象时指针比较的行为。此代码取决于它们是否正常工作,即使您在不同对象之间复制也是如此。

从实际意义上讲,代码很好。当指针不指向同一对象时,复制是升序还是降序都无关紧要,因此比较的结果无关紧要。重要的是,代码不会做一些非常可怕的事情,比如使过程崩溃或发送核发射代码。 C 标准并不禁止这样做,但它不太可能在任何实际实现中。大多数实现只是比较原始地址,任何奇怪的实现只会返回一个不可预测的值,但没有任何副作用。

最新更新