2009-07-29 12 views
87

¿Cuál es la diferencia entre memmove y memcpy? ¿Cuál usas habitualmente y cómo?¿Cuál es la diferencia entre memmove y memcpy?

+0

revertido debido Worrier reintrodujo mala gramática. Él puede editar las etiquetas nuevamente si lo desea. – Welbog

+2

Me parece que ustedes dos corrieron en una edición y Worrier no cambió el cuerpo del original :) – bdonlan

+0

Tenga en cuenta los problemas que pueden surgir: https://lwn.net/Articles/414467/ –

Respuesta

121

Con memcpy, el destino no puede superponerse a la fuente en absoluto. Con memmove puede. Esto significa que memmove podría ser un poco más lento que memcpy, ya que no puede hacer las mismas suposiciones. Por ejemplo, memcpy siempre copia direcciones de menor a mayor. Si el destino se superpone después de la fuente, esto significa que algunas direcciones se sobrescribirán antes de copiarse. memmove detectaría esto y copiaría en la otra dirección, de mayor a menor, en este caso. Sin embargo, comprobar esto y cambiar a otro algoritmo (posiblemente menos eficiente) lleva tiempo.

+1

cuando se usa memcpy, ¿cómo puedo garantizar que las direcciones src y dest no se superpongan? ¿Debo personalmente asegurarme de que src y dest no se superpongan? – Alcott

+6

@Alcott, no use memcpy si no sabe que no se superponen: use memmove en su lugar. Cuando no hay superposición, memmove y memcpy son equivalentes (aunque memcpy puede ser muy, muy, muy rápido). – bdonlan

+0

Puede usar la palabra clave 'restringir' si está trabajando con arreglos largos y desea proteger su proceso de copiado. Por ejemplo, si su método toma como parámetros matrices de entrada y salida, y debe verificar que el usuario no pase la misma dirección como entrada y salida. Lea más aquí http://stackoverflow.com/questions/776283/what-does-the-restrict-keyword-mean-in-c – DanielHsH

9

Uno maneja los destinos superpuestos y el otro no.

20

De la página del manual memcpy.

El memcpy() Función de copias n bytes de src área de memoria a la zona de memoria dest. Las áreas de memoria no deben superponerse a . Use memmove (3) si las áreas de memoria se superponen.

26

memmove puede manejar la superposición de memoria, memcpy no puede.

Considere

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

Obviamente, el origen y el destino ahora se superponen, estamos sobrescribiendo "-bar" con la "barra". Es un comportamiento indefinido usando memcpy si la fuente y el destino se superponen, por lo que en este caso necesitamos memmove.

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

¿por qué primero explotaría? –

+4

@ultraman: Porque PUEDE haberse implementado usando assembley de bajo nivel que requiere que la memoria no se superponga. Si lo hace, podría, por ejemplo, generar una señal o una excepción de hardware para el procesador que aborta la aplicación. La documentación especifica que no maneja la condición, pero el estándar no especifica qué sucederá cuando estas condiciones estén cargadas (esto se conoce como comportamiento indefinido). El comportamiento indefinido puede hacer cualquier cosa. –

+0

con gcc 4.8.2, incluso memcpy también acepta la superposición de punteros de origen y destino y funciona bien. – JagsVG

-10

La diferencia técnica se ha documentado bastante bien en las respuestas anteriores. Las diferencias prácticas también deben tenerse en cuenta:

Memcpy necesitará aproximadamente el doble de la cantidad de memoria para realizar la misma tarea, pero el memmove podría tomar mucho más tiempo que memcpy.

Ejemplo:

Digamos que usted tiene una lista de 100 elementos en la memoria, teniendo 100 MB de memoria. Ahora desea descartar la primera partida por lo que sólo tiene 99.

establecimiento de memoria requerirá la original de 100 MB y un adicional de 99 MB para su nueva lista de 99 artículos. Aproximadamente 199 MB en total para realizar la operación, pero debe ser muy rápido.

Memmove, en el peor de los casos, requerirá los 100 MB originales y moverá cada elemento 1 de la dirección de memoria a la vez. Esto solo requiere los 100 MB originales, pero será significativamente más lento que Memcpy.

Por supuesto, crear un nuevo puntero para apuntar al segundo elemento de su lista logrará el mismo efecto de "soltar" el primer elemento de su lista, pero el ejemplo muestra las diferencias en memcpy y memmove.

- EDITAR para ayudar a aclarar MI RESPUESTA CRAPPY -
Sí, las implementaciones de memcpy() y memmove() probablemente no difieren en el uso de memoria (que realmente no saben), pero ¿cómo se utiliza ellos afectarán enormemente el uso de la memoria de su programa. Eso es lo que quise decir con las diferencias prácticas de memcpy() y memmove().

int SIZE = 100; 
Item *ptr_item = (Item *) malloc(size_of(Item) * SIZE); 
Item *ptr_src_item = ptr_item + 1; 
Item *ptr_dst_item = ptr_item; 
memmove(ptr_dst_item, ptr_src_item, SIZE - 1); 

Esto crea su lista de elementos con el primer elemento faltante. Esto esencialmente no requiere más memoria para su programa que lo que se necesita para asignar el bloque de memoria ptr_item original. No puede hacer esto usando memcpy() ... si lo hiciera, su programa necesitaría asignar aproximadamente el doble de memoria.

int SIZE = 100; 
Item *ptr_src_item = (Item *) malloc(size_of(Item) * SIZE); 
Item *ptr_dst_item = (Item *) malloc(size_of(Item) * (SIZE - 1)); 
memcpy(ptr_dst_item, ptr_src_item, SIZE - 1); 

En este segundo bloque de código, el programa requerirá el doble de memoria que el primer bloque. Sin embargo, este segundo bloque debe funcionar significativamente más rápido que el primer bloque, especialmente a medida que aumenta TAMAÑO.

Así es como yo estaba tratando de explicar las diferencias prácticas en los dos ... pero ¿tal vez estoy equivocado con esto también?

+10

Tiene memcpy y memmove mezclados; tampoco requiere memoria extra más allá de un mínimo de espacio de pila. memmove comprueba si la fuente tiene una dirección inferior a la del destino; si es así, copia desde el final hacia atrás; si no, copia desde el principio hacia adelante. No se necesitan almacenamientos intermedios temporales. –

+1

Debería haber vuelto a escribir su respuesta en lugar de tener básicamente dos partes conflictivas y confusas de su publicación, una en la que es bastante inflexible sobre _ "el doble de la cantidad de memoria" _ y más adelante donde _ _ "probablemente no difieren en el uso de memoria (realmente no lo sé) "_. Decídase o al menos investigue antes – MickyD

9

La principal diferencia entre memmove() y memcpy() es que en memmove() un búfer - memoria temporal - se utiliza, por lo que no hay riesgo de solapamiento. Por otro lado, memcpy() copia directamente los datos de la ubicación que apunta la fuente a la ubicación señalada por el destino . (http://www.cplusplus.com/reference/cstring/memcpy/)

Considere los siguientes ejemplos:

  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 se esperaba, esto va a imprimir:

    stackoverflow 
    stackstacklow 
    stackstacklow 
    
  2. Sin embargo, en este ejemplo, los resultados no será el mismo:

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

    Salida:

    stackoverflow 
    stackstackovw 
    stackstackstw 
    

Se debe a que "memcpy()" hace lo siguiente:

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

buen ejemplo muestra bien la diferencia – bjackfly

+0

¡Pero parece que la salida que mencionó está invertida! – kumar

+0

Cuando ejecuto el mismo programa, obtengo el siguiente resultado: stackoverflow stackstackstw stackstackstw // significa que NO HAY diferencia en la salida entre memcpy y memmove – kumar

0

Intenté funcionar por encima de código: La razón principal quiero saber la diferencia de memcpy y 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; 
} 

da debajo de la salida

stackoverflow 
stackstackovw 
stackstackstw 

Ahora he reemplazado con memmovememcpy y me dio la misma salida

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

Salida:

stackoverflow 
stackstackovw 
stackstackstw 
+0

Eso solo da un único punto de datos en una plataforma. Es muy posible que encuentre (en su plataforma) que 'memcpy (first + 5, first, 7)' se comporta de forma diferente a 'memmove' con los mismos argumentos. –

5

simplemente fr om el estándar ISO/IEC: 9899 está bien descrito.

7.21.2.1 La función memcpy

[...]

2 La función copia memcpy n caracteres del objeto apuntado por s2 en el objeto apuntado por s1. Si la copia se realiza entre objetos que se superponen, el comportamiento no está definido.

Y

7.21.2.2 La función memmove

[...]

2 La función copia memmove n caracteres del objeto apuntado por s2 en el objeto apuntado por s1. La copia se lleva a cabo como si los n caracteres del objeto apuntados por s2 se copiaran primero en una matriz temporal de n caracteres que no se se superponen los objetos apuntados por s1 y s2, y luego los n caracteres del matriz temporal se copian en el objeto señalado por s1.

Cuál usualmente uso según la pregunta, depende de qué función necesito.

en texto plano memcpy() no permite s1 y s2 a solaparse, mientras memmove() hace.

0

cadena de caracteres [] = "stackoverflow";

char *first, *second; 

first = string; 

second = string; 

puts(string); 

o/p - Stackoverflow

memcpy(first+5, first,7); 

puts(first); 

Aquí 7 caracteres apuntados por segundo es decir, "stackov" pegado en 5 posición resultante

o/p - stackstackovw

memcpy(second+5, second, 7); 

puts(second); 

aquí la cadena de entrada es "stackstackovw", 7 caracteres apuntados por segundo (i.e "stackst") se copia en una memoria intermedia, entonces pegada en el lugar de 5 resultante

o/p - stackstackstw

0 ----- + 5
stackst pila w

0

Hay son dos maneras obvias para implementar mempcpy(void *dest, const void *src, size_t n) (ignorando el 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]; 
    

En la primera implementación, la copia procede de direcciones bajas a altas, y en el segundo, de mayor a menor. Si el rango que se va a copiar se superpone (como ocurre cuando se desplaza un framebuffer, por ejemplo), solo una dirección de operación es correcta y la otra sobrescribirá las ubicaciones de las que posteriormente se leerá.

Una implementación de memmove(), en su forma más simple, probará dest<src (de alguna manera dependiente de la plataforma), y ejecutará la dirección apropiada de memcpy().

El código de usuario no puede hacer eso, por supuesto, porque incluso después de convertir src y dst en algún tipo de puntero concreto, no apuntan (en general) al mismo objeto y, por lo tanto, no se pueden comparar. Pero la biblioteca estándar puede tener suficiente conocimiento de plataforma para realizar dicha comparación sin causar un Comportamiento no definido.


Tenga en cuenta que en la vida real, las implementaciones tienden a ser significativamente más complejo, para obtener el máximo rendimiento de las transferencias más grandes (cuando lo permita la alineación) y/o una buena utilización de caché de datos. El código anterior es solo para hacer el punto lo más simple posible.

0

memmove puede ocuparse de la superposición de regiones de origen y destino, mientras que memcpy no. Entre los dos, memcpy es mucho más eficiente. Entonces, es mejor usar memcpy si puedes.

Referencia: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Caín, (Stanford Introducción Sistemas de Conferencia - 7) Hora: 36:00

+0

[Esta respuesta] (http://stackoverflow.com/a/28624068/2410359) dice "posiblemente un smidge más rápido" y proporciona datos cuantitativos que indican solo una pequeña diferencia. Esta respuesta afirma que uno es "mucho más eficiente". ¿Cuánto más eficiente ha encontrado el más rápido para ser? Por cierto: supongo que te refieres a 'memcpy()' y no a 'memcopy()'. – chux

+0

El comentario está basado en la conferencia del Dr. Jerry Cain. Le pediría que escuchara su conferencia a las 36:00, solo 2-3 minutos serán suficientes. Y gracias por la captura. :RE – Ehsan

Cuestiones relacionadas