Respuestas:
Con memcpy
, el destino no puede superponerse a la fuente en absoluto. Con memmove
él 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 puede copiar direcciones de menor a mayor. Si el destino se superpone después del origen, esto significa que algunas direcciones se sobrescribirán antes de copiarlas. 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.
i = i++ + 1
que indefinido; el compilador no le prohíbe escribir exactamente ese código, pero el resultado de esa instrucción puede ser cualquier cosa y diferentes compiladores o CPU mostrarán valores diferentes aquí.
memmove
puede manejar la memoria superpuesta, memcpy
no puede.
Considerar
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Obviamente, la fuente y el destino ahora se superponen, estamos sobrescribiendo "-bar" con "bar". Es un comportamiento indefinido que usa memcpy
si el origen y el destino se superponen, por lo que en este caso los casos lo necesitamos memmove
.
memmove(&str[3],&str[4],4); //fine
La principal diferencia entre memmove()
y memcpy()
es que en memmove()
un búfer se usa memoria temporal, por lo que no hay riesgo de superposición. Por otro lado, memcpy()
copia directamente los datos de la ubicación señalada por la fuente a la ubicación señalada por el destino . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Considere los siguientes ejemplos:
#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 esperaba, esto se imprimirá:
stackoverflow
stackstacklow
stackstacklow
Pero en este ejemplo, los resultados no serán los mismos:
#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
Es porque "memcpy ()" hace lo siguiente:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
memmove()
se requiera la implementación de para usar un búfer. Tiene perfectamente derecho a moverse en el lugar (siempre que cada lectura se complete antes de cualquier escritura en la misma dirección).
Suponiendo que tendría que implementar ambos, la implementación podría verse así:
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
}
}
Y esto debería explicar bastante bien la diferencia. memmove
siempre copia de tal manera, que es todavía seguro si src
y dst
solapamiento, mientras que memcpy
simplemente no le importa lo que dice la documentación cuando se utiliza memcpy
, las dos áreas de memoria no debe solapamiento.
Por ejemplo, si las memcpy
copias "de adelante hacia atrás" y los bloques de memoria están alineados como este
[---- src ----]
[---- dst ---]
copiar el primer byte de src
que dst
ya se destruye el contenido de los últimos bytes src
antes de que éstos se hayan copiado. Solo copiar "al revés" conducirá a resultados correctos.
Ahora intercambie src
y dst
:
[---- dst ----]
[---- src ---]
En ese caso, solo es seguro copiar "de adelante hacia atrás", ya que copiar "de atrás hacia adelante" ya se destruiría src
cerca de su frente al copiar el primer byte.
Es posible que haya notado que la memmove
implementación anterior ni siquiera prueba si realmente se superponen, solo verifica sus posiciones relativas, pero eso solo hará que la copia sea segura. Como memcpy
suele utilizar la forma más rápida posible de copiar memoria en cualquier sistema, memmove
normalmente se implementa 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);
}
}
A veces, si memcpy
siempre se copia "de adelante hacia atrás" o "de atrás hacia adelante", memmove
también se puede usar memcpy
en uno de los casos superpuestos, pero memcpy
incluso se puede copiar de una manera diferente dependiendo de cómo se alinean los datos y / o cuántos datos se van a colocar. copiado, por lo que incluso si probó cómo se memcpy
copia en su sistema, no puede confiar en que el resultado de la prueba sea siempre correcto.
¿Qué significa eso para ti a la hora de decidir a cuál llamar?
A menos que esté seguro de eso src
y dst
no se superponga, llame, memmove
ya que siempre obtendrá resultados correctos y, por lo general, es lo más rápido posible para el caso de copia que necesita.
Si está seguro de eso src
y dst
no se superpone, llame, memcpy
ya que no importará a cuál llame para obtener el resultado, ambos funcionarán correctamente en ese caso, pero memmove
nunca será más rápido que memcpy
y si tiene mala suerte, incluso puede sea más lento, por lo que solo puede ganar llamando memcpy
.
simplemente de la norma ISO / IEC: 9899 está bien descrito.
7.21.2.1 La función memcpy
[...]
2 La función memcpy copia n caracteres del objeto apuntado por s2 en el objeto apuntado por s1. Si la copia tiene lugar entre objetos que se superponen, el comportamiento no está definido.
Y
7.21.2.2 La función memmove
[...]
2 La función memmove copia n caracteres del objeto apuntado por s2 en el objeto apuntado por s1. La copia tiene lugar como si los n caracteres del objeto señalado por s2 se copiaran primero en una matriz temporal de n caracteres que no se superpusieran a los objetos señalados por s1 y s2, y luego los n caracteres de la matriz temporal se copiaran en el objeto apuntado por s1.
Cuál uso habitualmente de acuerdo con la pregunta, depende de la funcionalidad que necesite.
En texto sin formato memcpy()
no permite s1
y se s2
superpone, mientras que memmove()
sí.
Hay dos formas obvias de implementar mempcpy(void *dest, const void *src, size_t n)
(ignorando el valor de retorno):
for (char *p=src, *q=dest; n-->0; ++p, ++q)
*q=*p;
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 la segunda, de altas a bajas. Si el rango que se va a copiar se superpone (como es el caso cuando se desplaza un framebuffer, por ejemplo), entonces solo una dirección de operación es correcta, y la otra sobrescribirá las ubicaciones que posteriormente se leerán.
Una memmove()
implementación, 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 la conversión src
y dst
de 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 la plataforma para realizar dicha comparación sin causar un comportamiento indefinido.
Tenga en cuenta que en la vida real, las implementaciones tienden a ser significativamente más complejas, para obtener el máximo rendimiento de transferencias más grandes (cuando la alineación lo permite) y / o una buena utilización de la caché de datos. El código anterior es solo para hacer el punto de la manera más simple posible.
memmove puede lidiar con regiones de origen y destino superpuestas, mientras que memcpy no. Entre los dos, memcpy es mucho más eficiente. Por lo tanto, es mejor USAR memcpy si puede.
Referencia: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain, (Conferencia de Stanford Intro Systems - 7) Hora: 36:00
memcpy()
y no memcopy()
.