web-dev-qa-db-ja.com

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

memmovememcpyの違いは何ですか?普段どれをどのように使用していますか?

108
ultraman

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

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

150
bdonlan

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
31
nos

memcpy manページから。

Memcpy()関数は、メモリ領域srcからメモリ領域destにnバイトをコピーします。メモリ領域はオーバーラップしないでください。メモリ領域が重複する場合は、 memmove (3)を使用します。

22
John Carter

memmove()memcpy()の主な違いは、memmove() abuffer-一時メモリ-使用されるため、重複するリスクはありません。一方、memcpy()は、sourceが指す場所から宛先。 ( http://www.cplusplus.com/reference/cstring/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
13
Mirac Suzgun

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

10
KPexEA

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

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が重なる場合でも安全であるように常にコピーしますが、memcpymemcpy、2つのメモリ領域必須ではない重複。

例えば。 memcpyが「前から後ろ」にコピーし、メモリブロックがこのように整列される場合

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

srcの最初のバイトをdstにコピーすると、srcの最後のバイトの内容は、コピーされる前にすでに破棄されています。 「前から後ろへ」コピーするだけで正しい結果が得られます。

srcdstを入れ替えます:

[---- 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を呼び出します。ただし、memmovememcpyよりも速くなることはありません。運が悪い場合はさらに遅くなる可能性があるため、memcpyの呼び出しに勝つことができます。

7
Mecki

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()は重複を許可します。

6
dhein

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

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

0
Ehsan

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

0
chetan h

mempcpy(void *dest, const void *src, size_t n)を実装する2つの明らかな方法があります(戻り値を無視します):

  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を具体的なポインタ型にキャストした後でも、(一般的に)同じオブジェクトを指し示していないので、ユーザーコードはもちろんできません。比較する。しかし、標準ライブラリには、Undefined Behaviourを引き起こさずにそのような比較を実行するのに十分なプラットフォーム知識があります。


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

0
Toby Speight

上記のコードを実行してみました:memcpymemmoveの違いを知りたい主な理由。

#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

memmovememcpyに置き換えたところ、同じ出力が得られました。

#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
0
sobin thomas