Qual é a diferença entre memmove
e memcpy
? Qual você costuma usar e como?
Qual é a diferença entre memmove
e memcpy
? Qual você costuma usar e como?
Respostas:
Com memcpy
, o destino não pode sobrepor a fonte. Com memmove
ele pode. Isso significa que memmove
pode ser um pouco mais lento do que memcpy
, pois não pode fazer as mesmas suposições.
Por exemplo, memcpy
sempre pode copiar endereços de baixo para alto. Se o destino se sobrepuser após a origem, isso significa que alguns endereços serão substituídos antes da cópia. memmove
detectaria isso e copiaria na outra direção - de alto para baixo - nesse caso. No entanto, verificar isso e mudar para outro algoritmo (possivelmente menos eficiente) leva tempo.
i = i++ + 1
é indefinido; o compilador não proíbe que você escreva exatamente esse código, mas o resultado dessa instrução pode ser qualquer coisa e diferentes compiladores ou CPUs mostrarão valores diferentes aqui.
memmove
pode lidar com sobreposição de memória, memcpy
não pode.
Considerar
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Obviamente, a origem e o destino agora se sobrepõem, estamos substituindo "-bar" por "bar". É um comportamento indefinido usando memcpy
se a origem e o destino se sobrepõem; nesse caso, precisamos memmove
.
memmove(&str[3],&str[4],4); //fine
A principal diferença entre memmove()
e memcpy()
é que em memmove()
um buffer - memória temporária - é usada, portanto, não há risco de sobreposição. Por outro lado, memcpy()
copia diretamente os dados do local apontado pela fonte para o local apontado pelo destino . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Considere os seguintes exemplos:
#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, isso será impresso:
stackoverflow
stackstacklow
stackstacklow
Mas neste exemplo, os resultados não serão os mesmos:
#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;
}
Resultado:
stackoverflow
stackstackovw
stackstackstw
Isso ocorre porque "memcpy ()" faz o seguinte:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
memmove()
é necessária para usar um buffer. É perfeitamente permitido mover-se no local (desde que cada leitura seja concluída antes de qualquer gravação no mesmo endereço).
Supondo que você teria que implementar os dois, a implementação poderia ser assim:
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 maneira, que ainda é seguro se src
e se dst
sobrepõe, embora memcpy
apenas não importe como a documentação diz ao usar memcpy
, as duas áreas de memória não devem se sobrepor.
Por exemplo, se as memcpy
cópias "da frente para trás" e os blocos de memória estiverem alinhados como este
[---- src ----]
[---- dst ---]
copiando o primeiro byte src
para dst
já destrói o conteúdo dos últimos bytes de src
antes que estes tenham sido copiadas. Apenas copiar "de trás para frente" levará a resultados corretos.
Agora troque src
e dst
:
[---- dst ----]
[---- src ---]
Nesse caso, só é seguro copiar "frente para trás", pois a cópia "de trás para frente" já destruiria src
perto de sua frente ao copiar o primeiro byte.
Você deve ter notado que a memmove
implementação acima nem testa se eles realmente se sobrepõem, apenas verifica suas posições relativas, mas somente isso tornará a cópia segura. Como memcpy
geralmente usa a maneira mais rápida possível de copiar memória em qualquer sistema, memmove
geralmente é 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 "volta para frente", memmove
também pode ser usado memcpy
em um dos casos sobrepostos, mas memcpy
pode ser copiado de maneira diferente, dependendo de como os dados estão alinhados e / ou quantos dados devem ser copiado, portanto, mesmo se você testou como as memcpy
cópias em seu sistema, não é possível confiar nesse resultado para estar sempre correto.
O que isso significa para você ao decidir para quem ligar?
A menos que você tenha certeza disso src
e dst
não se sobreponha, ligue memmove
, pois sempre levará a resultados corretos e geralmente é o mais rápido possível para o caso de cópia necessário.
Se você tem certeza disso src
e dst
não se sobrepõe, ligue memcpy
, pois não importa para quem você deseja o resultado, ambos funcionarão corretamente nesse caso, mas memmove
nunca serão mais rápidos do que memcpy
e se você tiver azar, pode até seja mais lento, para que você possa ganhar apenas chamadas memcpy
.
simplesmente a partir da norma ISO / IEC: 9899, está bem descrita.
7.21.2.1 A função memcpy
[...]
2 A função memcpy copia n caracteres do objeto apontado por s2 para o objeto apontado por s1. Se a cópia ocorrer entre objetos que se sobrepõem, o comportamento é indefinido.
E
7.21.2.2 A função memmove
[...]
2 A função memmove copia n caracteres do objeto apontado por s2 para o objeto apontado por s1. A cópia ocorre como se os n caracteres do objeto apontado por s2 fossem primeiro copiados para uma matriz temporária de n caracteres que não se sobrepusesse aos objetos apontados por s1 e s2 e, em seguida, os n caracteres da matriz temporária sejam copiados para o objeto apontado por s1.
Qual deles eu costumo usar de acordo com a pergunta depende de qual funcionalidade eu preciso.
Em texto sem formatação memcpy()
, não permite s1
e s2
se sobrepõe, enquanto memmove()
faz.
Existem duas maneiras óbvias de implementar mempcpy(void *dest, const void *src, size_t n)
(ignorando o 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];
Na primeira implementação, a cópia prossegue dos endereços baixo para o alto e, na segunda, do alto para o baixo. Se o intervalo a ser copiado se sobrepor (como é o caso ao rolar um buffer de quadros, por exemplo), apenas uma direção da operação está correta e a outra substituirá os locais que serão lidos posteriormente.
Uma memmove()
implementação, na sua forma mais simples, testará dest<src
(de alguma maneira dependente da plataforma) e executará a direção apropriada de memcpy()
.
O código do usuário não pode fazer isso, é claro, porque mesmo depois da conversão src
e dst
de algum tipo de ponteiro concreto, eles não apontam (em geral) para o mesmo objeto e, portanto, não podem ser comparados. Mas a biblioteca padrão pode ter conhecimento de plataforma suficiente para realizar essa comparação sem causar comportamento indefinido.
Observe que, na vida real, as implementações tendem a ser significativamente mais complexas, para obter o desempenho máximo de transferências maiores (quando o alinhamento permitir) e / ou boa utilização do cache de dados. O código acima é apenas para esclarecer o ponto da maneira mais simples possível.
O memmove pode lidar com regiões de origem e destino sobrepostas, enquanto o memcpy não pode. Entre os dois, o memcpy é muito mais eficiente. Então, é melhor USAR o memcpy, se puder.
Referência: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain, (Palestra sobre sistemas de introdução de Stanford - 7) Horário: 36:00
memcpy()
e não memcopy()
.