【问题标题】:Regarding implementation of memmove关于 memmove 的实现
【发布时间】:2013-11-05 13:10:43
【问题描述】:

我正在查看 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;
}

【问题讨论】:

标签: c portability memmove


【解决方案1】:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:
    1. 括号的解释在这里:What do the parentheses around a function name mean?

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

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

    【讨论】:

    • 该代码适用于具有平坦地址空间的现代平台。 Steve Jessop 在他对我的一个老问题的回答中对 C++ 的std::less 有很好的解释(我在对这个问题的回答中链接到了这个问题)。
    猜你喜欢
    • 2021-03-03
    • 1970-01-01
    • 2011-04-04
    • 2021-09-26
    • 2021-01-20
    • 2012-10-31
    • 2018-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多