回答:
を使用するmemcpy
と、宛先はソースとまったく重複できません。memmove
それができます。これは、同じ仮定を行うことができないため、memmove
よりもわずかに遅くなる可能性があることを意味しmemcpy
ます。
たとえば、memcpy
アドレスを常に低から高にコピーする場合があります。宛先がソースの後に重複する場合、これは、一部のアドレスがコピーされる前に上書きされることを意味します。memmove
この場合、これを検出し、逆方向(高から低)にコピーします。ただし、これを確認して別の(おそらく効率が悪い)アルゴリズムに切り替えるには時間がかかります。
i = i++ + 1
です。コンパイラーはそのコードを正確に記述することを禁止しませんが、その命令の結果は何でも可能であり、異なるコンパイラーまたはCPUはここに異なる値を示します。
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()
にすることであるバッファ一時メモリを- -使用されているので、重複の恐れがありません。一方、ソースによってポイントされた場所から宛先によってポイントされた場所にデータを直接コピーします。(http://www.cplusplus.com/reference/cstring/memcpy/)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, 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
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
ドキュメントでを使用するときに注意する必要がないのmemcpy
は、2つのメモリ領域が重複してはならないということです。
たとえば、memcpy
「前から後ろへ」のコピーとメモリブロックがこのように配置されている場合
[---- src ----]
[---- dst ---]
src
to の最初のバイトをコピーすると、これらがコピーされる前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が指すオブジェクトからn文字をs1が指すオブジェクトにコピーします。重複するオブジェクト間でコピーが行われた場合の動作は未定義です。
そして
7.21.2.2 memmove関数
[...]
2 memmove関数は、s2が指すオブジェクトからn文字をs1が指すオブジェクトにコピーします。コピーは、s2が指すオブジェクトのn文字が、s1とs2が指すオブジェクトと重ならないn文字の一時配列に最初にコピーされた後、一時配列のn文字が s1が指すオブジェクト。
質問に応じて通常どちらを使用するかは、必要な機能によって異なります。
プレーンテキストでmemcpy()
は、重複は許可されませんがs1
、s2
重複されmemmove()
ます。
実装するには、2つの明白な方法がありますmempcpy(void *dest, const void *src, size_t n)
(戻り値は無視されます)。
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
た後、具体的なポインター型にしたとしても、それらは(一般に)同じオブジェクトを指し示さず、比較できないためです。しかし、標準ライブラリは、未定義の動作を引き起こさずにそのような比較を実行するのに十分なプラットフォームの知識を持つことができます。
実際の実装では、実装が大幅に複雑になる傾向があり、より大きな転送(アライメントが許可されている場合)や最大のデータキャッシュの利用から最大のパフォーマンスを得られることに注意してください。上記のコードは、ポイントをできるだけ簡単にするためのものです。
memmoveは重複するソース領域と宛先領域を処理できますが、memcpyは処理できません。2つのうち、memcpyははるかに効率的です。したがって、可能であればmemcpyを使用することをお勧めします。
参照:https ://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain、(Stanford Intro Systems Lecture-7)時間:36:00
memcpy()
ではなく、と思いますmemcopy()
。