Diferencia entre memcpy y memmove

Resuelto
leoliom Mensajes publicados 187 Estado Miembro -  
leoliom Mensajes publicados 187 Estado Miembro -
Hola, he leído muchos documentos, he visto muchos ejemplos pero es imposible entender las cosas de superposición y otras. Quiero entender de tal manera que pueda prever lo que el programa me mostrará

Configuración: iPad / Chrome 98.0.4758.97

9 respuestas

jeannets Mensajes publicados 28379 Fecha de registro   Estado Colaborador Última intervención   Ambassadeur 6 600
 
Hola,

Memcpy es una copia de memoria en otra dirección, sin destruir el original

https://man7.org/linux/man-pages/man3/memcpy.3.html

Mientras que Memmove es un desplazamiento de memoria, a diferencia de Memcpy, borra la fuente..

https://www.man7.org/linux/man-pages/man3/memmove.3.html

Los atributos definen la fuente, el destino y el tamaño de los valores desplazados o copiados...

Por supuesto, para que todo esté sincronizado, hay que conocer la lección y los valores en movimiento. Hacer ejemplos en papel cuadriculado..
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
He leído también este sitio, pero no entendí. Como ustedes dicen, la única forma de entender es a través de un esquema, pero yo no sé, me cuesta un poco.

Quizás un pequeño ejemplo me ayude a comprender mejor.
0
Dalfab Mensajes publicados 638 Fecha de registro   Estado Miembro Última intervención   102
 
Las 2 funciones solo se distinguen en el caso de que haya superposición. Es cuando hay celdas que están a la vez en la fuente a copiar y en el destino.
Ejemplo, quieres copiar las 4 primeras celdas de la siguiente tabla en las 4 últimas:
+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+

Si utilizas
memmove()
obtendrás el siguiente resultado (las 4 últimas reciben correctamente lo que había en las 4 primeras)
+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 1 | 2 | 3 | 4 |
+---+---+---+---+---+---+---+

Si utilizas
memcpy()
, esta función es un poco "tonta", va rápido pero no "piensa" mucho. ¡Puede darte el siguiente resultado:
+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 1 | 2 | 3 | 1 |
+---+---+---+---+---+---+---+
Es una suposición, intenta entender lo que se perdió y trata de escribir un código que lo haga, o intenta escribir tú mismo una función
memcpy()
y observa por qué el final no es correcto.

Así que si no hay superposición, las funciones son equivalentes (pero
memcpy()
es más rápida), si hay superposición la función
memcpy()
puede tener un resultado incorrecto.
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
According to your example:
int tab[10] = {1, 2, 3, 4, 5, 6, 7}; int tab2[10]; strncpy(tab2,tab1,4); memmove(tab + 3, tab2, strlen(tab)) memcpy(tab + 3, tab2, strlen(tab))


Is it correct?

I didn't understand the sentence It's when some elements are both in the source to copy and in the destination.

For example in your example, for which number is there an overlap?
0
Dalfab Mensajes publicados 638 Fecha de registro   Estado Miembro Última intervención   102
 
No has entendido, de hecho. Solo hay un único array y se hacen copias dentro del array. Si hay dos arrays, entonces
memcpy()
y
memmove()
son indistinguibles.
Y
strncpy()
y
strlen()
son funciones que se aplican a cadenas de caracteres. Aquí son arrays de enteros, no tienen nada que ver y estas funciones hacen locuras en tu código.
Mi ejemplo es:
int tab[7] = {1, 2, 3, 4, 5, 6, 7}; memmove( &tab[3], &tab[0], 4*sizeof(int) ); // copiar tab[0]tab[1]tab[2]tab[3] en tab[3]tab[4]tab[5]tab[6]. for ( int i = 0 ; i < 7 ; ++i ) printf( "%d ", tab[i] );
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
Creo que está bien, he comprendido la sutileza entre los dos, me tomó mucho tiempo antes de entender
Gracias
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
Bonjour,

Para entender el funcionamiento de
memcpy
y
memmove
, he revisado su implementación en C, pero curiosamente no siempre hacen lo mismo. Para
memmove
, funciona, pero no para
memcpy
.

Ejemplo :

 #include <stdio.h> #include <string.h> void * Memcpy(void* dst, const void* src, unsigned int cnt) { char *pszDest = (char *)dst; const char *pszSource =( const char*)src; if((pszDest!= NULL) && (pszSource!= NULL)) { while(cnt) //hasta cnt { //Copiar byte por byte *(pszDest++)= *(pszSource++); --cnt; } } return dst; } int main(void){ char chaine[7] = "aabbcc"; Memcpy(chaine + 2, chaine, 4); printf("%s\n", chaine); return 0; } 
  • Memcpy
    devuelve
    aaaaaa
    ;
  • memcpy
    devuelve
    aaaabb
    .


Si tomo otra implementación en Internet :

 #include <string.h> #include <stdio.h> void myMemCpy(void *dest, void *src, size_t n){ // Convertir las direcciones de src y dest a (char *) char *csrc = (char *)src; char *cdest = (char *)dest; // Copiar el contenido de src[] a dest[] for (int i = 0; i < n; i++) cdest[i] = csrc[i]; } int main(void){ char str1[7] = "aabbcc"; myMemMove(str1 + 2, str1, 4); printf("New string: %s\n", str1); return 0; } 
  • mymencpy
    devuelve
    aaaaaa
    ;
  • memcpy
    devuelve
    aaaabb
    .


No entiendo, su implementación en C me parecía correcta. ¿Qué está mal? ¿Qué le falta a estas funciones para que hagan el mismo trabajo que
memcpy
?
0
mamiemando Mensajes publicados 33228 Fecha de registro   Estado Moderador Última intervención   7 940
 
Hola,

¿Qué hacen
memmove
y
memcpy
?

De manera general, basta con mirar lo que dicen
man memmove
y
man memcpy
(que puedes buscar en Google o en un terminal si estás en Linux)
  • memcpy
    copia un bloque de memoria de una dirección a otra dirección. El bloque de destino debe estar preasignado en memoria para recibir el bloque fuente, de lo contrario, generalmente obtendrás un error de segmentación. La copia se realiza típicamente byte a byte (y para recordar, un carácter ASCII (
    char
    ) ocupa un byte, por lo que esta función es muy similar a
    strcpy
    y, por lo tanto, se encuentra en
    <string.h>
    ): leemos el byte fuente actual, lo copiamos en el byte de destino correspondiente y repetimos la operación hasta haber tratado todo el bloque.
  • Oficialmente,
    memmove
    mueve un bloque de memoria de una dirección a otra dirección. El bloque de destino debe estar preasignado en memoria para recibir el bloque fuente, de lo contrario, generalmente obtendrás un error de segmentación. No hay ninguna garantía sobre lo que quedará en el bloque de origen, pero la mayoría de las implementaciones simplemente lo dejan tal como está para mejorar el rendimiento (y para estas implementaciones,
    memmove
    hace exactamente lo mismo que
    memcpy
    cuando los bloques fuente y destino no se superponen). En estas situaciones, usamos más
    memmove
    por la legibilidad del programa. Sin embargo, el bloque fuente ya no debe ser leído porque dependiendo de la implementación, puede haber sido reinicializado.


¿Qué pasa si los bloques se superponen?

Ahora, veamos con más detalle qué sucede si el bloque fuente y el bloque destino se superponen. Aquí, hay que ser cuidadoso, ya que puedes temer efectos secundarios (típicamente si escribes en el área que estás a punto de leer):
  • Según esta página,
    memmove
    utiliza un búfer intermedio para protegerse de estos efectos secundarios.
  • Según esta página, este no es el caso de
    memcpy
    , por lo que hay que tener cuidado con lo que se hace.


¿Por qué existe una función
strcpy
y
memcpy
?

Hay varias razones, y antes de comenzar es importante entender cómo se almacena una cadena de características en memoria. El problema de fondo es que una cadena de caracteres puede ser arbitrariamente larga (a diferencia de un
uint32_t
que por definición ocupa exactamente 32 bits o 4 bytes). ¿Cómo hacer un seguimiento del tamaño?

La elección que se ha hecho es marcar la cadena con un carácter terminal
'\0'
(que corresponde al
uint8_t
0
. Todas las funciones
str*
(
strcpy
,
strlen
, ...) y
printf
se apoyan en este carácter terminal para saber cuándo detenerse. Esto significa que la cadena
"abc"
ocupa 4 bytes
{'a', 'b', 'c', '\0'}
y no 3 como podría pensarse. Esto significa que no se debe olvidar este famoso carácter terminal cada vez que se manipulan cadenas. Sin él, las funciones
str*
seguirán buscando (por suerte) un byte que valga 0 y que a menudo está fuera de un área de memoria asignada, lo que provoca un error de segmentación.

Si volvemos a
memcpy
, nos damos cuenta de que podemos reproducir
strcpy
siempre y cuando conozcamos la longitud de la cadena a copiar (sin olvidar su
'\0'
terminal).

Como
memcpy
está diseñada para manipular un bloque de memoria arbitrario -de ahí el tipo
const void *
para el bloque fuente (solo lectura) y el tipo
void *
para el bloque destino (lectura y escritura)- (en particular, algo que no es una cadena y por lo tanto, algo que no necesariamente está terminado por
'\0'
), se debe indicar cuántos bytes copiar. Ese es el papel de su tercer parámetro.

Regreso a tu código

En tu caso, dado que manipulas cadenas, no es suficiente copiar las letras para que funcione bien, hay que asegurarse de que la cadena que vas a copiar o que vas a mostrar tenga su carácter terminal
'\0'
. Eso es algo que garantiza
strcpy
, pero no lo hace
memcpy
.

Buena suerte
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
Ah, entonces las implementaciones de estas funciones no son buenas porque no se tiene en cuenta el carácter terminal de fin de cadena

¿Debo modificar el código para que añada este carácter al final de la copia para que funcione?
0
Dalfab Mensajes publicados 638 Fecha de registro   Estado Miembro Última intervención   102
 
>>> Ah, entonces las implementaciones de estas funciones no son buenas porque no se tiene en cuenta el carácter terminal de fin de cadena.
No. ¿Has visto en alguna parte que memcpy() debía poner un terminador de cadena?

>> ¿Debo modificar el código para que añada al final de la copia este carácter para que funcione?
No.

La función memcpy(), la función mymemcpy() y la función Memcpy() son las 3 conformes.
La norma dice:
- si no hay solapamiento, la función debe copiar (puedes verificar que las 3 funciones efectúan bien la copia).
- si hay solapamiento, el resultado es totalmente impredecible (así que el resultado es completamente aleatorio, ¡no tiene sentido comparar los resultados en este caso!)

Es totalmente posible que la función memcpy() sea diferente a sí misma si cambias el contexto.
 char str1[7] = "aabbcc"; char str2[7] = "aabbcc"; memcpy(str1 + 2, str1, 4); printf("Nueva cadena: %s\n", str1); memcpy(str2 + 2, str2, 4); printf("Nueva cadena: %s\n", str2);
Este código puede devolver 2 resultados diferentes con aparentemente los mismos datos de entrada, o tener un resultado diferente en la versión de depuración y en la versión de liberación , inténtalo. Es inútil querer reproducir lo que ves en el caso en que la función tiene un resultado indefinido!
0
mamiemando Mensajes publicados 33228 Fecha de registro   Estado Moderador Última intervención   7 940 > Dalfab Mensajes publicados 638 Fecha de registro   Estado Miembro Última intervención  
 
Para completar lo que dice Dalfab:
  • las funciones
    str*
    suponen que el bloque de memoria a copiar es de tamaño arbitrario pero puede considerarse como una cadena de caracteres terminada por
    '\0'
    ;
  • las funciones
    mem*
    no suponen un carácter de parada (lo que permite copiar cualquier secuencia de bytes, pero necesita un tamaño para saber cuándo detenerse ;
  • en todos los casos, estas funciones solo deben leer y escribir en áreas de memoria asignadas por el programa, de lo contrario se desencadenará un error de segmentación.
0
leoliom Mensajes publicados 187 Estado Miembro 2
 
Gracias por todas estas respuestas, ahora lo entiendo mejor.
0