質問
memmove
とmemcpy
の違いは何ですか?どちらを通常どのように使用しますか?
解決
memcpy
の場合、宛先はソースとまったく重複できません。 memmove
で可能です。これは、同じ仮定を立てることができないため、<=>は<=>よりもわずかに遅い可能性があることを意味します。
たとえば、<=>は常にアドレスを低から高にコピーします。コピー元の後にコピー先が重複する場合、これはコピーする前に一部のアドレスが上書きされることを意味します。 <=>はこれを検出し、この場合、他の方向(高から低)にコピーします。ただし、これをチェックして別の(おそらく効率が低い)アルゴリズムに切り替えるには時間がかかります。
他のヒント
memmove
は重複メモリを処理できますが、memcpy
は処理できません。
検討
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
明らかにソースとデスティネーションはオーバーラップしているため、上書きしています <!> quot; -bar <!> quot; <!> quot; bar <!> quot;で。ソースが<=>の場合、動作は未定義です と宛先が重複するため、この場合は<=>が必要です。
memmove(&str[3],&str[4],4); //fine
memmove()
とmemcpy()
の主な違いは、<=>でバッファ-一時メモリ-が使用されるため、重複するリスクがないことです。一方、<=>は、ソースが指す場所から宛先が指す場所にデータを直接コピーします。 ( http://www.cplusplus.com/reference/cstring/memcpy/ )
次の例を検討してください:
-
#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *first, *second; first = string; second = string; puts(string); memcpy(first+5, first, 5); puts(first); memmove(second+5, second, 5); puts(second); return 0; }
予想どおり、これは印刷されます:
stackoverflow stackstacklow stackstacklow
-
この例では、結果は同じではありません:
#include <stdio.h> #include <string.h> int main (void) { char string [] = "stackoverflow"; char *third, *fourth; third = string; fourth = string; puts(string); memcpy(third+5, third, 7); puts(third); memmove(fourth+5, fourth, 7); puts(fourth); return 0; }
出力:
stackoverflow stackstackovw stackstackstw
<!> quot; memcpy()<!> quot;以下を実行します。
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
1つは重複する宛先を処理し、もう1つは処理しません。
ISO / IEC:9899規格から簡単に説明されています。
7.21.2.1 memcpy関数
[...]
2 memcpy関数は、s2が指すオブジェクトからn個の文字を s1が指すオブジェクト。 重複するオブジェクト間でコピーが行われる場合、動作は未定義です。
そして
7.21.2.2 memmove関数
[...]
2 memmove関数は、s2が指すオブジェクトからn個の文字を s1が指すオブジェクト。コピーは、オブジェクトのn文字のように行われます s2が指すは、最初にコピーされないn文字の一時配列にコピーされます s1とs2が指すオブジェクトをオーバーラップしてから、 一時配列は、s1が指すオブジェクトにコピーされます。
質問に応じて通常使用するものは、必要な機能性によって異なります。
プレーンテキストでは、memcpy()
ではs1
とs2
をオーバーラップできませんが、memmove()
ではオーバーラップできます。
両方を実装する必要があると仮定すると、実装は次のようになります。
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src) {
// Copy from front to back
}
}
void mempy ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src != (uintptr_t)dst) {
// Copy in any way you want
}
}
そして、これは違いをかなりよく説明するはずです。 memmove
は常にsrc
とdst
が重なった場合でも安全であるようにコピーしますが、memcpy
は<=>を使用するときにドキュメントが言うように、2つのメモリ領域重複してはいけません。
E.g。 if <=>コピー<!> quot; front to back <!> quot;メモリブロックはこのように整列されます
[---- src ----]
[---- dst ---]
<=>の最初のバイトを<=>にコピーすると、コピーされる前に<=>の最後のバイトの内容が既に破棄されます。 <!> quot; back to front <!> quot;のみをコピーします。正しい結果につながります。
今スワップ<=>と<=>:
[---- dst ----]
[---- src ---]
その場合、<!> quot; front to back <!> quot;をコピーするだけです。コピー<!> quot; back to front <!> quot;最初のバイトをコピーするときに、すでにその前面近くの<=>を破壊します。
上記の<=>実装では、実際にオーバーラップするかどうかもテストされず、相対的な位置をチェックするだけですが、それだけでコピーが安全になります。 <=>は通常、あらゆるシステムでメモリをコピーするために可能な最速の方法を使用するため、<=>は通常次のように実装されます。
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst
&& (uintptr_t)src + count > (uintptr_t)dst
) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src
&& (uintptr_t)dst + count > (uintptr_t)src
) {
// Copy from front to back
} else {
// They don't overlap for sure
memcpy(dst, src, count);
}
}
<=>が常に<!> quot; front to back <!> quot;をコピーする場合または<!> quot; back to front <!> quot;、<=>は重複するケースのいずれかで<=>を使用することもありますが、<=>はデータの配置方法や/またはコピーするデータの量です。システムで<=>コピー方法をテストしたとしても、そのテスト結果が常に正しいとは限りません。
どちらを呼び出すかを決定するとき、それはあなたにとって何を意味しますか?
-
<=>と<=>が重ならないことが確実でない限り、<=>を呼び出してください。常に正しい結果が得られ、通常はコピーケースで可能な限り高速です。
-
<=>と<=>が重複しないことがわかっている場合は、どちらを呼び出すかは問題ではないため、<=>を呼び出します。その場合、どちらも正しく動作します。ただし、<=>は<=>より速くなることはありません。運が悪い場合はさらに遅くなる可能性があるため、<=>の呼び出しにのみ勝つことができます。
mempcpy(void *dest, const void *src, size_t n)
を実装する2つの明らかな方法があります(戻り値を無視):
-
for (char *p=src, *q=dest; n-->0; ++p, ++q) *q=*p;
-
char *p=src, *q=dest; while (n-->0) q[n]=p[n];
最初の実装では、コピーは低アドレスから高アドレスに進み、2番目では高アドレスから低アドレスに進みます。コピーする範囲が重複している場合(たとえば、フレームバッファーをスクロールする場合など)、操作の1つの方向のみが正しく、他の操作は後で読み取られる場所を上書きします。
A memmove()
実装は、最も簡単な方法でdest<src
をテストし(プラットフォームに依存する方法で)、memcpy()
の適切な指示を実行します。
ユーザーコードはもちろんできません。src
とdst
を具体的なポインター型にキャストした後でも、それらは(一般に)同じオブジェクトを指しておらず、比較できないためです。 。しかし、標準ライブラリは、Undefined Behaviourを引き起こさずにそのような比較を実行するのに十分なプラットフォーム知識を持っています。
実生活では、より大きな転送(アライメントが許可されている場合)および/または適切なデータキャッシュ利用から最大のパフォーマンスを得るために、実装は非常に複雑になる傾向があることに注意してください。上記のコードは、ポイントをできるだけ単純にするためのものです。
memmoveは重複するソース領域と宛先領域を処理できますが、memcpyは処理できません。 2つの中で、memcpyははるかに効率的です。したがって、可能であればmemcpyを使用することをお勧めします。
参照: https://www.youtube.com/watch?v=Yr1YnOVG -4g ジェリーケイン博士(Stanford Intro Systems Lecture-7)時間:36:00