memmove
とmemcpy
の違いは何ですか?普段どれをどのように使用していますか?
memcpy
を使用すると、宛先はソースとまったく重複できません。 memmove
で可能です。これは、memmove
がmemcpy
よりもわずかに遅い可能性があることを意味します。同じ仮定を立てることができないためです。
たとえば、memcpy
は常にアドレスを低から高にコピーします。コピー元の後にコピー先が重複する場合、これはコピーする前に一部のアドレスが上書きされることを意味します。この場合、memmove
はこれを検出し、他の方向(高から低)にコピーします。ただし、これを確認して別の(おそらく効率が低い)アルゴリズムに切り替えるには時間がかかります。
memmove
は重複メモリを処理できますが、memcpy
は処理できません。
検討する
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
明らかにソースとデスティネーションはオーバーラップし、「-bar」を「bar」で上書きしています。送信元と送信先が重なる場合はmemcpy
を使用する未定義の動作なので、この場合はmemmove
が必要です。
memmove(&str[3],&str[4],4); //fine
memmove()
とmemcpy()
の主な違いは、memmove()
abuffer-一時メモリ-使用されるため、重複するリスクはありません。一方、memcpy()
は、sourceが指す場所から宛先。 ( 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
「memcpy()」が次のことを行うためです。
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
1つは重複する宛先を処理し、もう1つは処理しません。
両方を実装する必要があると仮定すると、実装は次のようになります。
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
はmemcpy
、2つのメモリ領域必須ではない重複。
例えば。 memcpy
が「前から後ろ」にコピーし、メモリブロックがこのように整列される場合
[---- src ----]
[---- dst ---]
src
の最初のバイトをdst
にコピーすると、src
の最後のバイトの内容は、コピーされる前にすでに破棄されています。 「前から後ろへ」コピーするだけで正しい結果が得られます。
src
とdst
を入れ替えます:
[---- dst ----]
[---- src ---]
その場合、「前から後ろ」をコピーすると最初のバイトをコピーするときにすでに前の近くのsrc
が破壊されるため、「前から後ろ」をコピーするだけです。
上記のmemmove
実装は、実際にオーバーラップするかどうかもテストせず、相対的な位置を確認するだけですが、それだけでコピーが安全になります。 memcpy
は通常、あらゆるシステムでメモリをコピーするために可能な最速の方法を使用するため、memmove
は通常次のように実装されます。
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);
}
}
場合によっては、memcpy
が常に「前から後ろ」または「後ろから前」をコピーする場合、memmove
は重複するケースのいずれかでmemcpy
を使用する場合がありますが、memcpy
データの配置方法やコピーするデータ量に応じて異なる方法で、システムでmemcpy
コピーの方法をテストしたとしても、そのテスト結果が常に正しいとは限りません。
どちらを呼び出すかを決定するとき、それはあなたにとって何を意味しますか?
src
とdst
が重複しないことが確実でない限り、memmove
を呼び出してください。常に正しい結果が得られ、コピーケースで可能な限り高速です。必要です。
src
とdst
が重複しないことがわかっている場合は、どちらを呼び出しても結果が問題にならないため、memcpy
を呼び出します。ただし、memmove
はmemcpy
よりも速くなることはありません。運が悪い場合はさらに遅くなる可能性があるため、memcpy
の呼び出しに勝つことができます。
iSO/IEC:9899規格から簡単に説明されています。
7.21.2.1 memcpy関数
[...]
2 memcpy関数は、s2が指すオブジェクトからs1が指すオブジェクトにn個の文字をコピーします。 重複するオブジェクト間でコピーが行われる場合、動作は未定義です。
そして
7.21.2.2 memmove関数
[...]
2 memmove関数は、s2が指すオブジェクトからn文字をs1が指すオブジェクトにコピーします。コピーが行われますs2が指すオブジェクトのn文字が、最初に、指すオブジェクトが重複しないn文字の一時配列にコピーされますto s1およびs2によって、そして一時配列からのn文字がs1によって指されたオブジェクトにコピーされます。
私が質問に応じて通常どれを使用するかは、必要な機能性によって異なります。
プレーンテキストでは、memcpy()
は_s1
_と_s2
_の重複を許可しませんが、memmove()
は重複を許可します。
memmoveは重複するソース領域と宛先領域を処理できますが、memcpyは処理できません。 2つの中で、memcpyははるかに効率的です。したがって、可能であればmemcpyを使用することをお勧めします。
参照: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain、(Stanford Intro Systems Lecture-7)時間:36:00
char string [] = "stackoverflow";
char *first, *second;
first = string;
second = string;
puts(string);
o/p-スタックオーバーフロー
memcpy(first+5, first,7);
puts(first);
ここでは、2番目のポイントが指す7文字、つまり+5の位置に貼り付けられた「stackov」
o/p-Stackstackovw
memcpy(second+5, second, 7);
puts(second);
ここで入力文字列は「stackstackovw」で、2番目のポイントが指す7文字(つまり「stackst」)がバッファにコピーされ、+ 5の場所に貼り付けられます
o/p-stackstackstw
0 ----- + 5
stack stackst w
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つの方向のみが正しく、他の操作は後で読み取られる場所を上書きします。
memmove()
実装は、最も単純な場合、_dest<src
_をテストし(プラットフォームに依存する方法で)、memcpy()
の適切な方向を実行します。
src
とdst
を具体的なポインタ型にキャストした後でも、(一般的に)同じオブジェクトを指し示していないので、ユーザーコードはもちろんできません。比較する。しかし、標準ライブラリには、Undefined Behaviourを引き起こさずにそのような比較を実行するのに十分なプラットフォーム知識があります。
実生活では、より大きな転送(アライメントが許可される場合)および/または適切なデータキャッシュ利用から最大のパフォーマンスを得るために、実装は非常に複雑になる傾向があることに注意してください。上記のコードは、ポイントをできるだけ単純にするためのものです。
上記のコードを実行してみました:memcpy
とmemmove
の違いを知りたい主な理由。
#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
memmove
を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,7);
puts(first);
memcpy(second+5, second, 7);
puts(second);
return 0;
}
出力:
stackoverflow
stackstackovw
stackstackstw