В чем разница между memmove и memcpy?

в чем разница между memmove и memcpy? Какой вы обычно используете и как?

11 ответов


С memcpy, назначение не может перекрывать источник вообще. С memmove он может. Это значит, что memmove может быть немного медленнее, чем memcpy, поскольку он не может сделать те же предположения.

например, memcpy всегда может копировать адреса от низкого до высокого. Если назначение перекрывается после источника, это означает, что некоторые адреса будут перезаписаны перед копированием. memmove обнаружит это и скопирует в другом направлении - от максимума до минимума - в этом случае. Однако проверка этого и переход на другой (возможно, менее эффективный) алгоритм требует времени.


memmove смогите отрегулировать перекрывая память,memcpy не могу.

считают

char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up

очевидно, что источник и назначение теперь перекрываются, мы перезаписываем "бар" с "БАР". Это неопределенное поведение с использованием memcpy если источник и место назначения перекрывается, поэтому в этом случае нам нужно memmove.

memmove(&str[3],&str[4],4); //fine

с memcpy man-страница.

функция memcpy () копирует n байт из области памяти src в область памяти дест. Области памяти не должны частично покрывать. Использовать memmove(3) Если памяти области перекрываются.


основное различие между memmove() и memcpy(), что в memmove() a буфер - временная память-используется, поэтому нет риска перекрытия. С другой стороны,--6--> непосредственно копирует данные из местоположения, на которое указывает источник к месту, указанному назначения. (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

один обрабатывает перекрывающиеся назначения, другой-нет.


просто из стандарта ISO / IEC:9899 это хорошо описано.

7.21.2.1 функция memcpy

[...]

2 Функция memcpy копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. если копирование происходит между перекрывающимися объектами, поведение не определено.

и

7.21.2.2 функция memmove

[...]

2 Функция memmove копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. Копирование происходит как если бы n символов из объекта на которые указывает s2, сначала копируются во временный массив из n символов, который не перекрытие объекты, на которые указывают s1 и s2, а затем N символов из временный массив копируется в объект, на который указывает s1.

какой я обычно используйте acording к вопросу, зависит от того, какая функциональность мне нужна.

обычный текст 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 просто не волнует, как говорится в документации при использовании memcpy, две области памяти не должен перекрытие.

Е. Г. если memcpy копирует "спереди назад" и блоки памяти выровнено как это

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

затем копирование первого байта от src do dst уже уничтожает содержимое последних байтов src прежде чем они были скопированы. Поэтому здесь безопасно копировать только "назад к фронту".

поменяйтесь src и dst:

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

в этом случае безопасно копировать только "спереди назад", так как копирование" назад к фронту " уничтожит src рядом с его фронтом уже при копировании первого байта.

вы возможно, заметили, что memmove реализация выше даже не проверяет, действительно ли они перекрываются, она просто проверяет их относительные позиции, но только это сделает копию безопасной. As 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. если вы не знаете наверняка, что src и dst не пересекаются, называют memmove как это всегда приведет к правильным результатам, и обычно так же быстро, как это возможно случай экземпляра вы требуете.

  2. если вы точно знаете, что src и dst не пересекаются, называют memcpy поскольку не имеет значения, какой из них вы вызываете для результата, оба будут работать правильно в этом случае, но memmove никогда не будет быстрее, чем memcpy и если вам не повезет, это может быть даже медленнее, поэтому вы можете выиграть только вызов memcpy.


Я попытался запустить выше кода : основная причина, по которой я хочу знать разницу 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

char string [] = "stackoverflow";

char *first, *second;

first = string;

second = string;

puts(string);

o / p - stackoverflow

memcpy(first+5, first,7);

puts(first);

здесь 7 символов, указанных вторым i.е "стаков" наклеен на + 5 позицию в результате

o / p-stackstackovw

memcpy(second+5, second, 7);

puts(second);

здесь входная строка - "stackstackovw", 7 символов, указываемых секундой (i.e "stackst") копируется в буфер, затем вставляется в место +5 результирующего

o / p-stackstackstw

0-----+5
стек stackst w


существует два очевидных способа реализации 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];
    

в первой реализации копия переходит с низкого на высокий адреса, а во второй-с высокого на низкий. Если диапазон для копирования перекрывается (как, например, при прокрутке фреймбуффера), то корректно только одно направление работы, а другое будет перезаписывать местоположения, которые впоследствии считываться.

A memmove() реализация, в самом простом виде, будет тестировать dest<src (каким-то образом зависящим от платформы) и выполните соответствующее направление memcpy().

пользовательский код не может этого сделать, потому что даже после кастинга src и dst к некоторому конкретному типу указателя, они не указывают (вообще) на один и тот же объект и поэтому не могут быть сравнены. Но стандартная библиотека может иметь достаточно знаний платформы для выполнения такого сравнения, не вызывая Неопределенное Поведение.


обратите внимание, что в реальной жизни реализации, как правило, значительно сложнее, чтобы получить максимальную производительность от больших передач (когда позволяет выравнивание) и/или хорошее использование кэша данных. Приведенный выше код-это просто сделать точку зрения как можно более простой.


memmove может иметь дело с перекрывающимися областями источника и назначения, в то время как memcpy не может. Среди них memcpy намного эффективнее. Итак, лучше использовать memcpy, если можете.

ссылка:https://www.youtube.com/watch?v=Yr1YnOVG-4g Доктор Джерри Кейн, (Stanford Intro Systems Lecture - 7) Время: 36:00