私のシステムのmanページから:
void *memmove(void *dst, const void *src, size_t len);
説明
memmove() 関数は、文字列 src から文字列 dst に len バイトをコピーします。
2 つの文字列が重複する場合があります。コピーは常に非破壊的な方法で行われ
ます。
C99 標準から:
6.5.8.5 2 つのポインターを比較すると、結果は、指しているオブジェクトのアドレス空間内の相対位置によって異なります。オブジェクト型または不完全型への 2 つのポインターが両方とも同じオブジェクトを指している場合、または両方が同じ配列オブジェクトの最後の要素の 1 つ後ろを指している場合、それらは等しいと見なされます。指しているオブジェクトが同じ集合体オブジェクトのメンバーである場合、後で宣言された構造体メンバーへのポインターは、構造体で以前に宣言されたメンバーへのポインターよりも大きく、添字値が大きい配列要素へのポインターは、同じ配列の要素へのポインターよりも大きくなります。より低い添字値で。同じ共用体オブジェクトのメンバーへのすべてのポインターは等しいと比較されます。式の場合
P
が配列オブジェクトの要素を指し、式 Q が同じ配列オブジェクトの最後の要素を指している場合、ポインター式はQ+1
より大きいと比較 されますP
。それ以外の場合、動作は undefinedです。
強調は私のものです。
厳密なエイリアシングの問題を軽減するために、引数dst
とsrc
をポインターに変換できますがchar
、同じブロック内を指している場合に正しい順序でコピーを行うために、異なるブロック内を指している可能性のある 2 つのポインターを比較することは可能ですか? ?
明らかな解決策は ですが、 と が異なるブロックを指しているif (src < dst)
場合は未定義です。「未定義」とは、条件が 0 または 1 を返すと想定してはならないことを意味します (これは、標準の語彙では「未指定」と呼ばれていました)。src
dst
代替手段はif ((uintptr_t)src < (uintptr_t)dst)
であり、これは少なくとも指定されていませんが、src < dst
が定義されたときに と同等であることを標準が保証しているかどうかはわかりません(uintptr_t)src < (uintptr_t)dst)
。ポインター比較は、ポインター演算から定義されます。たとえば、加算に関するセクション 6.5.6 を読んだとき、ポインター演算はuintptr_t
演算と逆の方向に進む可能性がp
あるように思えchar*
ます。
((uintptr_t)p)+1==((uintptr_t)(p-1)
これはほんの一例です。一般的に言えば、ポインターを整数に変換するときに保証されることはほとんどないようです。
これはmemmove
、コンパイラと共に提供されているため、純粋に学術的な質問です。実際には、コンパイラの作成者は、未定義のポインタ比較を未指定の動作に単純に促進するか、関連するプラグマを使用してコンパイラにそれらをmemmove
正しくコンパイルさせることができます。たとえば、この実装には次のスニペットがあります。
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* As author/maintainer of libc, take advantage of the
* fact that we know memcpy copies forwards.
*/
return memcpy(dst, src, len);
}
memmove
標準 C で効率的に実装できないことが本当である場合、標準が未定義の動作で行き過ぎていることの証明として、この例を引き続き使用したいと思います。