Pergunta

O que é a diferença entre memmove e memcpy? Qual deles você costuma usar e como?

Foi útil?

Solução

Com memcpy, o destino não pode sobrepor-se a fonte de todo. Com memmove ele pode. Isto significa que memmove pode ser muito ligeiramente mais lento do que memcpy, como ele não pode fazer as mesmas premissas.

Por exemplo, memcpy pode sempre copiar endereços de baixa para alta. Se o destino se sobrepõe após a fonte, isso significa que alguns endereços será substituído antes copiado. memmove iria detectar isso e copiar em outra direção - de alto a baixo - neste caso. No entanto, verificar isso e mudar para outro (possivelmente menos eficiente) algoritmo leva tempo.

Outras dicas

memmove pode lidar com a memória sobrepostos, memcpy não pode.

Considere

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

Obviamente, a origem eo destino agora sobreposição, estamos substituição "-Bar" com "bar". É um comportamento indefinido usando memcpy se a fonte e destino sobreposição Portanto, neste casos caso de precisarmos memmove.

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

A partir do href="http://linux.die.net/man/3/memcpy" rel="noreferrer"> memcpy página homem .

Os memcpy () cópias de função n bytes da área de memória src para a área de memória dest. As áreas de memória não deve sobreposição. Use memmove (3) se a memória áreas se sobrepõem.

A principal diferença entre memmove() e memcpy() é que em memmove() um tampão - memória temporária - é usado, portanto, não há risco de sobreposição. Por outro lado, memcpy() diretamente copia os dados a partir da localização que é apontado pela source para o local apontado pelo destino . ( http://www.cplusplus.com/reference/cstring/memcpy/ )

Considere os seguintes exemplos:

  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;
    }
    

    Como você esperava, este irá imprimir:

    stackoverflow
    stackstacklow
    stackstacklow
    
  2. Mas neste exemplo, os resultados não serão o mesmo:

    #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;
    }
    

    Output:

    stackoverflow
    stackstackovw
    stackstackstw
    

É porque "memcpy ()" faz o seguinte:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw

Um alças sobrepostas destinos a outros não.

simplesmente a partir da ISO / IEC:. 9899 padrão, é bem descrito

7.21.2.1 A função memcpy

[...]

2 A função memcpy cópias n caracteres do objeto apontado por s2 para o objeto apontado por s1. Se a cópia ocorre entre objetos que se sobrepõem, o comportamento é indefinido.

E

7.21.2.2 A função memmove

[...]

2 A função memmove cópias n caracteres do objeto apontado por s2 para o objeto apontado por s1. Cópia ocorre como se os n caracteres do objeto apontado por s2 são primeiro copiados para uma matriz temporária de n caracteres que não faz sobreposição os objectos apontada por S1 e S2, e, em seguida, os n caracteres a partir da matriz temporária são copiadas para o objeto apontado por S1.

Qual deles eu costumo usar monitores segundo à pergunta, depende do que necessidade functionallity I.

No texto simples memcpy() não permite s1 e s2 a sobreposição, enquanto memmove() faz.

Assumindo que você teria que implementar ambos, a implementação poderia ser semelhante a que:

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
    }
}

E isso deve muito bem explicar a diferença. memmove sempre copia de tal forma, que ainda é seguro se src e dst sobreposição, enquanto memcpy apenas não se importa como a documentação diz ao usar memcpy, as duas áreas de memória não deve sobreposição.

por exemplo. se memcpy cópias "de frente para trás" e os blocos de memória estão alinhados como este

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

já copiando o primeiro byte de src para dst destrói o conteúdo dos últimos bytes de src antes que estes tenham sido copiadas. Apenas a cópia "de trás para frente" levará a resultados corretos.

Agora troca src e dst:

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

Nesse caso, é apenas seguro para copiar "frente para trás" como copiar "de trás para frente" destruiria src perto de sua frente já ao copiar o primeiro byte.

Você pode ter notado que a implementação memmove acima não mesmo teste se eles realmente fazem sobreposição, ele apenas verifica as suas posições relativas, mas isso só vai fazer o cofre cópia. Como memcpy geralmente usa a forma mais rápida possível copiar memória em qualquer sistema, memmove é geralmente bastante implementado como:

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);
    }
}

Às vezes, se memcpy sempre copia "frente para trás" ou "de trás para frente", memmove também pode usar memcpy em um dos casos que se sobrepõem, mas memcpy pode até mesmo copiar de uma maneira diferente, dependendo de como os dados são alinhados e / ou a quantidade de dados a ser copiado, por isso mesmo que você testou como memcpy cópias em seu sistema, você não pode confiar em que o resultado do teste a ser sempre correto.

O que isso significa para você quando decidir qual a chamar?

  1. Se você não sabe com certeza que src e dst não se sobrepõem, chamada memmove como sempre levará a resultados corretos e geralmente é tão rápido quanto o que é possível para o caso de cópia que você necessita.

  2. Se você sabe com certeza que src e dst não se sobrepõem, chamada memcpy como ele vai não importa qual você chamar para o resultado, ambos irão funcionar corretamente nesse caso, mas memmove nunca vai ser mais rápido que memcpy e se você é azarado, pode até ser mais lenta, então você só pode ganhar chamando memcpy.

Existem duas maneiras óbvias para implementar mempcpy(void *dest, const void *src, size_t n) (ignorando o valor de retorno):

  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];
    

Na primeira implementação, a cópia de recursos provenientes de baixo para endereços altos, e, no segundo, de alto a baixo. Se o intervalo a ser copiado sobreposições (como é o caso quando a rolagem de um framebuffer, por exemplo), então apenas uma direção de operação é correta, ea outra irá substituir locais que posteriormente serão lidos do.

Uma implementação memmove(), na sua forma mais simples, vai testar dest<src (de alguma forma dependente da plataforma), e executar a direcção apropriada de memcpy().

O código do usuário não pode fazer isso, é claro, porque mesmo depois de lançar src e dst a algum tipo de ponteiro de concreto, eles não (em geral) ponto para o mesmo objeto e, portanto, não podem ser comparados. Mas a biblioteca padrão pode ter conhecimento plataforma suficiente para realizar tal comparação sem causar um comportamento indefinido.


Note que na vida real, as implementações tendem a ser significativamente mais complexo, para obter o máximo desempenho de maiores transferências (quando licenças de alinhamento) e / ou boa utilização do cache de dados. O código acima é apenas para fazer o ponto tão simples quanto possível.

memmove pode lidar com sobreposição de regiões de origem e destino, enquanto memcpy não pode. Entre os dois, memcpy é muito mais eficiente. Então, melhor usar memcpy-lo se você puder.

Referência: https://www.youtube.com/watch?v=Yr1YnOVG -4G Dr. Jerry Cain, (Stanford Intro Sistemas Palestra - 7) Horário: 36:00

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top