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) と記載されています。

2 つのポインターを比較すると、結果は、指しているオブジェクトのアドレス空間内の相対位置によって異なります。オブジェクト型または不完全型への 2 つのポインターが両方とも同じオブジェクトを指している場合、または両方が同じ配列オブジェクトの最後の要素の 1 つ後ろを指している場合、それらは等しく比較されます。指しているオブジェクトが同じ集約オブジェクトのメンバーである場合、後で宣言された構造体メンバーへのポインターは、構造体で以前に宣言されたメンバーへのポインターよりも大きく、添字値が大きい配列要素へのポインターは、同じ配列の要素へのポインターよりも大きくなります。より低い添字値で。同じ共用体オブジェクトのメンバーへのすべてのポインターは等しいと比較されます。式 P が配列オブジェクトの要素を指し、式 Q が同じ配列オブジェクトの最後の要素を指す場合、

これが説明で言及されているものかどうかはわかりませんが、これは移植性のない明確な原因の 1 つです。

別の実装では を使用する場合があります(uintptr_t)p2 < (uintptr_t)p1。次に、比較<は整数間の比較です。への変換uintptr_tにより、実装定義の結果が得られます。この型uintptr_tは C99 で導入されたもので、ポインターの表現を保持することが保証されている符号なし整数型です。

の完全に移植可能な実装でmemmove()は、3 番目のバッファーを使用して中間コピーを保持するか、比較を使用==します (使用する必要があるコンテキストで指定された結果が得られます)。

于 2013-10-26T11:59:49.323 に答える
5
  1. 括弧の説明はここにあります:関数名の周りの括弧はどういう意味ですか?

  2. p2 < p1とのp1 < p2 + n比較のため、移植性がありません。C 標準では、2 つのポインターが同じオブジェクトを指している場合のポインター比較の動作のみが定義されています。このコードは、異なるオブジェクト間でコピーしている場合でも、それらが適切に機能することに依存しています。

実用的な意味では、コードは問題ありません。ポインターが同じオブジェクトを指していない場合、コピーが昇順または降順で行われたかどうかは問題ではないため、比較の結果は無関係です。重要なのは、コードがプロセスをクラッシュさせたり、核発射コードを送信したりするなど、本当に恐ろしいことをしないことです。C 標準はこれを禁止していませんが、現実的な実装ではありそうにありません。ほとんどの実装は単に生のアドレスを比較するだけであり、奇妙な実装は予測できない値を返すだけですが、副作用はありません。

于 2013-10-26T12:00:09.790 に答える