5

我在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;
}
4

2 回答 2

9

函数的规范memmove()是它可以处理重叠的源和目标,但规范并没有说memmove()必须用指向同一内存块的指针(标准用语中的“对象”)来调用它。

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

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

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

不同的实现可能会使用(uintptr_t)p2 < (uintptr_t)p1. 那么比较<就是整数之间的比较。转换为uintptr_t提供实现定义的结果。该类型uintptr_t是在 C99 中引入的,是一种无符号整数类型,保证可以保存指针的表示形式。

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

于 2013-10-26T11:59:49.323 回答
5
  1. 括号的解释在这里:函数名周围的括号是什么意思?

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

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

于 2013-10-26T12:00:09.790 回答