Estoy tratando de entender la diferencia entre memcpy()
y memmove()
, y he leído el texto que memcpy()
no se ocupa de la fuente y el destino superpuestos, pero memmove()
sí lo hace.
Sin embargo, cuando ejecuto estas dos funciones en bloques de memoria superpuestos, ambos dan el mismo resultado. Por ejemplo, tome el siguiente ejemplo de MSDN en la memmove()
página de ayuda:
¿Hay un mejor ejemplo para comprender los inconvenientes memcpy
y cómo lo memmove
resuelve?
// crt_memcpy.c
// Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle
// it correctly.
#include <memory.h>
#include <string.h>
#include <stdio.h>
char str1[7] = "aabbcc";
int main( void )
{
printf( "The string: %s\n", str1 );
memcpy( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string
printf( "The string: %s\n", str1 );
memmove( str1 + 2, str1, 4 );
printf( "New string: %s\n", str1 );
}
Salida:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
memcpy
haríaassert
que las regiones no se superponen en lugar de cubrir intencionalmente errores en el código.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Respuestas:
No estoy completamente sorprendido de que su ejemplo no muestre un comportamiento extraño. Intente copiar
str1
en sustr1+2
lugar y vea qué sucede entonces. (Puede que en realidad no haga la diferencia, depende del compilador / bibliotecas).En general, memcpy se implementa de una manera simple (pero rápida). Simplísticamente, simplemente recorre los datos (en orden), copiando de una ubicación a otra. Esto puede provocar que se sobrescriba la fuente mientras se lee.
Memmove trabaja más para asegurarse de que maneja la superposición correctamente.
EDITAR:
(Desafortunadamente, no puedo encontrar ejemplos decentes, pero estos lo harán). Contraste las implementaciones de memcpy y memmove que se muestran aquí. memcpy simplemente realiza un bucle, mientras que memmove realiza una prueba para determinar en qué dirección realizar un bucle para evitar corromper los datos. Estas implementaciones son bastante simples. La mayoría de las implementaciones de alto rendimiento son más complicadas (implican copiar bloques de tamaño de palabra a la vez en lugar de bytes).
fuente
memmove
las llamadasmemcpy
en una rama después de probar los punteros: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/...memcpy
puede ser más rápido.La memoria
memcpy
no puede superponerse o corre el riesgo de un comportamiento indefinido, mientras que la memoriamemmove
puede superponerse.Algunas implementaciones de memcpy aún podrían funcionar para entradas superpuestas, pero no puede contar ese comportamiento. Mientras memmove debe permitir la superposición.
fuente
El hecho de
memcpy
que no tenga que lidiar con regiones superpuestas, no significa que no las aborde correctamente. La llamada con regiones superpuestas produce un comportamiento indefinido. El comportamiento indefinido puede funcionar completamente como espera en una plataforma; eso no significa que sea correcto o válido.fuente
memcpy
se implemente exactamente de la misma manera quememmove
. Es decir, quien escribió el compilador no se molestó en escribir unamemcpy
función única .Tanto memcpy como memove hacen cosas similares.
Pero para ver una diferencia:
da:
fuente
Su demo no expuso los inconvenientes de memcpy debido al compilador "malo", le hace un favor en la versión de depuración. Sin embargo, una versión de lanzamiento le ofrece el mismo resultado, pero debido a la optimización.
El registro
%eax
aquí se reproduce como un almacenamiento temporal, que "elegantemente" soluciona el problema de superposición.El inconveniente surge al copiar 6 bytes, bueno, al menos parte de él.
Salida:
Parece extraño, también es causado por la optimización.
Es por eso que siempre elijo
memmove
cuando intento copiar 2 bloques de memoria superpuestos.fuente
La diferencia entre
memcpy
ymemmove
es queen
memmove
, la memoria de origen del tamaño especificado se copia en el búfer y luego se mueve al destino. Entonces, si la memoria se superpone, no hay efectos secundarios.en caso de que
memcpy()
no haya memoria intermedia adicional para la memoria fuente. La copia se realiza directamente en la memoria, de modo que cuando hay superposición de memoria, obtenemos resultados inesperados.Estos pueden ser observados por el siguiente código:
Salida es:
fuente
Como ya se señaló en otras respuestas,
memmove
es más sofisticado quememcpy
tal que explica las superposiciones de memoria. El resultado de memmove se define como sisrc
se hubiera copiado en un búfer y luego se haya copiado en el búferdst
. Esto NO significa que la implementación real usa algún búfer, pero probablemente hace algo de aritmética de puntero.fuente
el compilador podría optimizar memcpy, por ejemplo:
Esta memoria puede optimizarse como:
x = *(int*)some_pointer;
fuente
int
accesos no alineados . En algunas arquitecturas (p. Ej., Cortex-M0), intentar obtener 32 bitsint
de una dirección que no es un múltiplo de cuatro provocará un bloqueo (peromemcpy
funcionaría). Si uno usará una CPU que permite el acceso no alineado o usará un compilador con una palabra clave que le indique al compilador que ensamble enteros de bytes extraídos por separado cuando sea necesario, podría hacer algo como#define UNALIGNED __unaligned
y luego `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
pero algunos sí, porque arreglan la copia durante la falla. Trabajé en un sistema como este, y me llevó un poco de tiempo entender por qué el rendimiento era tan pobre.*(int *)some_pointer
es una violación estricta de alias, pero probablemente quiere decir que el compilador generaría un ensamblado que copia un intEl código dado en los enlaces http://clc-wiki.net/wiki/memcpy para memcpy parece confundirme un poco, ya que no da el mismo resultado cuando lo implementé usando el siguiente ejemplo.
Salida:
Pero ahora puede entender por qué memmove se encargará de la superposición de problemas.
fuente
Borrador estándar C11
El borrador estándar del C11 N1570 dice:
7.24.2.1 "La función memcpy":
7.24.2.2 "La función memmove":
Por lo tanto, cualquier superposición
memcpy
conduce a un comportamiento indefinido, y cualquier cosa puede suceder: malo, nada o incluso bueno. Sin embargo, lo bueno es raro :-)memmove
sin embargo, dice claramente que todo sucede como si se usara un búfer intermedio, por lo que claramente las superposiciones están bien.std::copy
Sin embargo, C ++ es más indulgente y permite superposiciones: ¿std :: copy maneja los rangos superpuestos?fuente
memmove
usa una matriz temporal adicional de n, entonces, ¿usa memoria adicional? Pero, ¿cómo puede hacerlo si no le hemos dado acceso a ninguna memoria? (Está usando 2 veces la memoria).He intentado ejecutar el mismo programa usando eclipse y muestra una clara diferencia entre
memcpy
ymemmove
.memcpy()
no le importa la superposición de la ubicación de la memoria que da como resultado la corrupción de datos, mientrasmemmove()
que primero copiará los datos a la variable temporal y luego los copiará en la ubicación de la memoria real.Al intentar copiar datos de la ubicación
str1
astr1+2
, la salida dememcpy
es "aaaaaa
". La pregunta sería ¿cómo?memcpy()
copiará un byte a la vez de izquierda a derecha. Como se muestra en su programa "aabbcc
", todas las copias se realizarán de la siguiente manera,aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
primero copiará los datos a la variable temporal y luego los copiará a la ubicación de memoria real.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
La salida es
memcpy
:aaaaaa
memmove
:aaaabb
fuente
memmove()
copias en una ubicación intermedia. Simplemente debe copiar a la inversa cuando sea necesario.