Qual é a diferença entre memmove e memcpy?
Pergunta
O que é a diferença entre memmove
e memcpy
? Qual deles você costuma usar e como?
Solução
Com memcpy
, o destino não pode sobrepor-se a fonte de todo. Com memmove
ele pode. Isto significa que memmove
pode ser muito ligeiramente mais lento do que memcpy
, como ele não pode fazer as mesmas premissas.
Por exemplo, memcpy
pode sempre copiar endereços de baixa para alta. Se o destino se sobrepõe após a fonte, isso significa que alguns endereços será substituído antes copiado. memmove
iria detectar isso e copiar em outra direção - de alto a baixo - neste caso. No entanto, verificar isso e mudar para outro (possivelmente menos eficiente) algoritmo leva tempo.
Outras dicas
memmove
pode lidar com a memória sobrepostos, memcpy
não pode.
Considere
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Obviamente, a origem eo destino agora sobreposição, estamos substituição
"-Bar" com "bar". É um comportamento indefinido usando memcpy
se a fonte
e destino sobreposição Portanto, neste casos caso de precisarmos memmove
.
memmove(&str[3],&str[4],4); //fine
A principal diferença entre memmove()
e memcpy()
é que em memmove()
um tampão - memória temporária - é usado, portanto, não há risco de sobreposição. Por outro lado, memcpy()
diretamente copia os dados a partir da localização que é apontado pela source 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, este irá imprimir:
stackoverflow stackstacklow stackstacklow
-
Mas neste exemplo, os resultados não serão o mesmo:
#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; }
Output:
stackoverflow stackstackovw stackstackstw
É porque "memcpy ()" faz o seguinte:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
Um alças sobrepostas destinos a outros não.
simplesmente a partir da ISO / IEC:. 9899 padrão, é bem descrito
7.21.2.1 A função memcpy
[...]
2 A função memcpy cópias n caracteres do objeto apontado por s2 para o objeto apontado por s1. Se a cópia ocorre entre objetos que se sobrepõem, o comportamento é indefinido.
E
7.21.2.2 A função memmove
[...]
2 A função memmove cópias n caracteres do objeto apontado por s2 para o objeto apontado por s1. Cópia ocorre como se os n caracteres do objeto apontado por s2 são primeiro copiados para uma matriz temporária de n caracteres que não faz sobreposição os objectos apontada por S1 e S2, e, em seguida, os n caracteres a partir da matriz temporária são copiadas para o objeto apontado por S1.
Qual deles eu costumo usar monitores segundo à pergunta, depende do que necessidade functionallity I.
No texto simples memcpy()
não permite s1
e s2
a sobreposição, enquanto memmove()
faz.
Assumindo que você teria que implementar ambos, a implementação poderia ser semelhante a que:
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 forma, que ainda é seguro se src
e dst
sobreposição, enquanto memcpy
apenas não se importa como a documentação diz ao usar memcpy
, as duas áreas de memória não deve sobreposição.
por exemplo. se memcpy
cópias "de frente para trás" e os blocos de memória estão alinhados como este
[---- src ----]
[---- dst ---]
já copiando o primeiro byte de src
para dst
destrói o conteúdo dos últimos bytes de src
antes que estes tenham sido copiadas. Apenas a cópia "de trás para frente" levará a resultados corretos.
Agora troca src
e dst
:
[---- dst ----]
[---- src ---]
Nesse caso, é apenas seguro para copiar "frente para trás" como copiar "de trás para frente" destruiria src
perto de sua frente já ao copiar o primeiro byte.
Você pode ter notado que a implementação memmove
acima não mesmo teste se eles realmente fazem sobreposição, ele apenas verifica as suas posições relativas, mas isso só vai fazer o cofre cópia. Como memcpy
geralmente usa a forma mais rápida possível copiar memória em qualquer sistema, memmove
é geralmente bastante 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 "de trás para frente", memmove
também pode usar memcpy
em um dos casos que se sobrepõem, mas memcpy
pode até mesmo copiar de uma maneira diferente, dependendo de como os dados são alinhados e / ou a quantidade de dados a ser copiado, por isso mesmo que você testou como memcpy
cópias em seu sistema, você não pode confiar em que o resultado do teste a ser sempre correto.
O que isso significa para você quando decidir qual a chamar?
-
Se você não sabe com certeza que
src
edst
não se sobrepõem, chamadamemmove
como sempre levará a resultados corretos e geralmente é tão rápido quanto o que é possível para o caso de cópia que você necessita. -
Se você sabe com certeza que
src
edst
não se sobrepõem, chamadamemcpy
como ele vai não importa qual você chamar para o resultado, ambos irão funcionar corretamente nesse caso, masmemmove
nunca vai ser mais rápido quememcpy
e se você é azarado, pode até ser mais lenta, então você só pode ganhar chamandomemcpy
.
Existem duas maneiras óbvias para 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 de recursos provenientes de baixo para endereços altos, e, no segundo, de alto a baixo. Se o intervalo a ser copiado sobreposições (como é o caso quando a rolagem de um framebuffer, por exemplo), então apenas uma direção de operação é correta, ea outra irá substituir locais que posteriormente serão lidos do.
Uma implementação memmove()
, na sua forma mais simples, vai testar dest<src
(de alguma forma dependente da plataforma), e executar a direcção apropriada de memcpy()
.
O código do usuário não pode fazer isso, é claro, porque mesmo depois de lançar src
e dst
a algum tipo de ponteiro de concreto, eles não (em geral) ponto para o mesmo objeto e, portanto, não podem ser comparados. Mas a biblioteca padrão pode ter conhecimento plataforma suficiente para realizar tal comparação sem causar um comportamento indefinido.
Note que na vida real, as implementações tendem a ser significativamente mais complexo, para obter o máximo desempenho de maiores transferências (quando licenças de alinhamento) e / ou boa utilização do cache de dados. O código acima é apenas para fazer o ponto tão simples quanto possível.
memmove pode lidar com sobreposição de regiões de origem e destino, enquanto memcpy não pode. Entre os dois, memcpy é muito mais eficiente. Então, melhor usar memcpy-lo se você puder.
Referência: https://www.youtube.com/watch?v=Yr1YnOVG -4G Dr. Jerry Cain, (Stanford Intro Sistemas Palestra - 7) Horário: 36:00