memmoveとmemcpyの違いは何ですか?


回答:


162

を使用するmemcpyと、宛先はソースとまったく重複できません。memmoveそれができます。これは、同じ仮定を行うことができないため、memmoveよりもわずかに遅くなる可能性があることを意味しmemcpyます。

たとえば、memcpyアドレスを常に低から高にコピーする場合があります。宛先がソースの後に重複する場合、これは、一部のアドレスがコピーされる前に上書きされることを意味します。memmoveこの場合、これを検出し、逆方向(高から低)にコピーします。ただし、これを確認して別の(おそらく効率が悪い)アルゴリズムに切り替えるには時間がかかります。


1
memcpyを使用する場合、srcとdestのアドレスが重複しないことをどのように保証できますか?個人的に、srcとdestが重複しないことを確認する必要がありますか?
Alcott、2011

6
@Alcott、重複していないことがわからない場合はmemcpyを使用しないでください。代わりにmemmoveを使用してください。重複がない場合、memmoveとmemcpyは同等です(memcpyは非常に、非常に、非常にわずかに速い場合があります)。
bdonlan 2011

長い配列を使用していて、コピープロセスを保護したい場合は、 'restrict'キーワードを使用できます。たとえば、メソッドがパラメータとして入力配列と出力配列を取り、ユーザーが入力と出力に同じアドレスを渡していないことを確認する必要がある場合。もっとここで読むstackoverflow.com/questions/776283/...
DanielHsH

10
@DanielHsH 'restrict'は、コンパイラに約束するものです。コンパイラーによって強制されません。引数に 'restrict'を設定し、実際に重複している(またはより一般的には、複数の場所から派生したポインターから制限されたデータにアクセスする)場合、プログラムの動作は未定義であり、奇妙なバグが発生し、コンパイラー通常、警告は表示されません。
bdonlan 2015

@bdonlanコンパイラへの約束だけでなく、呼び出し元の要件でもあります。これは強制されない要件ですが、要件に違反した場合、予期しない結果が発生しても文句を言うことはできません。要件の違反は、未定義の場合と同様に、未定義の動作i = i++ + 1です。コンパイラーはそのコードを正確に記述することを禁止しませんが、その命令の結果は何でも可能であり、異なるコンパイラーまたはCPUはここに異なる値を示します。
Mecki

33

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

5
なぜ最初に爆破するのですか?

4
@ultraman:低レベルのアセンブリを使用して実装されている可能性があるため(メモリがオーバーラップしないことが必要)。もしそうなら、例えば、アプリケーションを打ち切るプロセッサへの信号またはハードウェア例外を生成することができます。ドキュメントでは、条件を処理しないことを指定していますが、標準では、これらの条件が無効化された場合の動作を指定していません(これは未定義の動作として知られています)。未定義の動作は何でも実行できます。
マーティンヨーク

gcc 4.8.2では、memcpyでもソースポインターと宛先ポインターのオーバーラップを受け入れ、正常に動作します。
GeekyJ 2015年

4
@jagsgediyaもちろんそうです。しかし、memcpyはこれをサポートしないように文書化されているため、その実装固有の動作に依存すべきではありません。そのため、memmove()が存在します。gccの別のバージョンでは異なる場合があります。gccがglibcでmemcpy()を呼び出すのではなくmemcpyをインライン化する場合は異なる場合があり、glibcの古いバージョンや新しいバージョンでは異なる場合があります。
番号

練習から、memcpyとmemmoveは同じことをしたようです。そのような深い未定義の動作。
ライフ

22

memcpyのマニュアルページを参照してください。

memcpy()関数は、メモリ領域srcからメモリ領域destにnバイトをコピーします。メモリ領域は重複してはいけません。メモリ領域が重複している場合は、memmove(3)を使用してください。


12

主な違いmemmove()とはmemcpy()にすることであるバッファ一時メモリを- -使用されているので、重複の恐れがありません。一方、ソースによってポイントされた場所から宛先によってポイントされた場所にデータを直接コピーします。(http://www.cplusplus.com/reference/cstring/memcpy/memmove()memcpy()

次の例を検討してください。

  1. #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
  2. ただし、この例では、結果は同じにはなりません。

    #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

2
しかし、あなたが言及した出力は逆転しているようです!!
kumar 2014年

1
私は、同じプログラムを実行すると、私は次のような結果を得る:memcpyをとMEMMOVE間の出力におけるNO差があるstackstackstw stackstackstw //手段をStackOverflowの
クマー

4
「それは、「memmove()」で、バッファ-一時メモリ-が使用されることです。」真実ではない。それは「まるで」のように言うので、それはそのように振る舞う必要があるということではなく、そのように振る舞う必要があるだけです。ほとんどのmemmove実装はXORスワップを実行するだけなので、これは確かに意味があります。
2015年

2
memmove()バッファの使用にはの実装が必要だとは思いません。同じアドレスに書き込む前に各読み取りが完了する限り、完全にインプレースで移動することができます。
Toby Speight 2016年

12

両方を実装する必要があるとすると、実装は次のようになります。

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常にそのような方法でコピーしますがsrcdst重複しても安全ですが、memcpyドキュメントでを使用するときに注意する必要がないのmemcpyは、2つのメモリ領域重複してはならないということです。

たとえば、memcpy「前から後ろへ」のコピーとメモリブロックがこのように配置されている場合

[---- src ----]
            [---- dst ---]

srcto の最初のバイトをコピーすると、これらがコピーされる前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、システムでどのようにコピーをテストしたとしても、そのテスト結果が常に正しいとは限りません。

どちらを呼び出すかを決めるときに、それはどういう意味ですか?

  1. それが重複しsrcdstいないことが確実でない限り、電話をかけるmemmoveと常に正しい結果が得られ、通常は必要なコピーケースで可能な限り高速です。

  2. それが確かでsrcdst重複しないことがわかっている場合はmemcpy、どちらを呼び出すかは問題ではないので呼び出してください。どちらの場合も結果は正しく機能しますが、memmove速度が速くなることはなくmemcpy、運が悪い場合は、遅くなるので、あなただけが通話に勝つことができますmemcpy


+1は、「ASCII図面」が、データを破壊せずに重複がない理由を理解するのに役立つためです
Scylardor

10

1つは重複する宛先を処理し、もう1つは処理しません。


6

単に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()は、重複は許可されませんがs1s2重複されmemmove()ます。


0

実装するには、2つの明白な方法がありますmempcpy(void *dest, const void *src, size_t n)(戻り値は無視されます)。

  1. for (char *p=src, *q=dest;  n-->0;  ++p, ++q)
        *q=*p;
  2. char *p=src, *q=dest;
    while (n-->0)
        q[n]=p[n];

最初の実装では、コピーは低アドレスから高アドレスに進み、2番目の実装では高アドレスから低アドレスに進みます。コピーする範囲が重複している場合(フレームバッファーをスクロールする場合など)、正しい方向の操作は1つだけであり、もう一方の方向は後で読み込まれる場所を上書きします。

memmove()実装は、その最も単純で、テストするdest<src(いくつかのプラットフォーム依存の方法で)、および適切な向きを実行しますmemcpy()

もちろん、ユーザーコードはそれを行うことはできません。キャストsrcdstた後、具体的なポインター型にしたとしても、それらは(一般に)同じオブジェクトを指し示さず、比較できないためです。しかし、標準ライブラリは、未定義の動作を引き起こさずにそのような比較を実行するのに十分なプラットフォームの知識を持つことができます。


実際の実装では、実装が大幅に複雑になる傾向があり、より大きな転送(アライメントが許可されている場合)や最大のデータキャッシュの利用から最大のパフォーマンスを得られることに注意してください。上記のコードは、ポイントをできるだけ簡単にするためのものです。


0

memmoveは重複するソース領域と宛先領域を処理できますが、memcpyは処理できません。2つのうち、memcpyははるかに効率的です。したがって、可能であればmemcpyを使用することをお勧めします。

参照:https ://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain、(Stanford Intro Systems Lecture-7)時間:36:00


この回答は、「たぶん、すごく速い」と言い、わずかな違いのみを示す定量的データを提供します。この回答は、1つが「はるかに効率的」であると断言します。どれだけ効率的で、より高速なものを見つけましたか?ところで:私はあなたが意味しているのmemcpy()ではなく、と思いますmemcopy()
chux-モニカを2016年

コメントは、ジェリー・ケイン博士の講演に基づいて行われました。時間36:00に彼の講義を聞くようにお願いします。2〜3分で十分です。そして、キャッチをありがとう。:D
Ehsan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.