Estou tentando entender a diferença entre memcpy()
e memmove()
, e li o texto que memcpy()
não cuida da origem e do destino sobrepostos memmove()
.
No entanto, quando executo essas duas funções em blocos de memória sobrepostos, eles fornecem o mesmo resultado. Por exemplo, considere o seguinte exemplo do MSDN na memmove()
página de ajuda: -
Existe um exemplo melhor para entender as desvantagens memcpy
e como memmove
resolver isso?
// 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 );
}
Resultado:
The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
memcpy
seriaassert
que as regiões não se sobreponham ao invés de intencionalmente encobrindo erros em seu código.The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
Respostas:
Não estou totalmente surpreso que seu exemplo não mostre comportamento estranho. Tente copiar
str1
astr1+2
vez e ver o que acontece em seguida. (Na verdade, pode não fazer diferença, depende do compilador / bibliotecas.)Em geral, o memcpy é implementado de maneira simples (mas rápida). Simplisticamente, ele apenas faz um loop sobre os dados (em ordem), copiando de um local para outro. Isso pode resultar na substituição da fonte enquanto está sendo lida.
O Memmove trabalha mais para garantir que ele lide com a sobreposição corretamente.
EDITAR:
(Infelizmente, não consigo encontrar exemplos decentes, mas eles servirão). Compare as implementações de memcpy e memmove mostradas aqui. O memcpy apenas faz um loop, enquanto o memmove executa um teste para determinar em qual direção executar o loop para evitar a corrupção dos dados. Essas implementações são bastante simples. A maioria das implementações de alto desempenho é mais complicada (envolve copiar blocos de tamanho de palavra por vez, em vez de bytes).
fonte
memmove
chamamemcpy
uma ramificação após testar os ponteiros: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/…memcpy
pode ser mais rápido.A memória
memcpy
não pode se sobrepor ou você corre o risco de comportamento indefinido, enquanto a memóriamemmove
pode se sobrepor.Algumas implementações do memcpy ainda podem funcionar para sobrepor entradas, mas você não pode contar com esse comportamento. Enquanto o memmove deve permitir sobreposição.
fonte
Só porque
memcpy
não precisa lidar com regiões sobrepostas, não significa que não lide com elas corretamente. A chamada com regiões sobrepostas produz um comportamento indefinido. O comportamento indefinido pode funcionar inteiramente como você espera em uma plataforma; isso não significa que seja correto ou válido.fonte
memcpy
seja implementado exatamente da mesma maneira quememmove
. Ou seja, quem escreveu o compilador não se incomodou em escrever umamemcpy
função única .Memcpy e memove fazem coisas semelhantes.
Mas, para ver uma diferença:
dá:
fonte
Sua demo não expôs os inconvenientes do memcpy por causa do compilador "ruim", ele faz um favor na versão Debug. Uma versão de lançamento, no entanto, fornece a mesma saída, mas por causa da otimização.
O registro
%eax
aqui é reproduzido como um armazenamento temporário, que corrige "elegantemente" o problema de sobreposição.A desvantagem surge ao copiar 6 bytes, bem, pelo menos parte dele.
Resultado:
Parece estranho, também é causado pela otimização.
É por isso que sempre escolho
memmove
ao tentar copiar 2 blocos de memória sobrepostos.fonte
A diferença entre
memcpy
ememmove
é queem
memmove
, a memória de origem do tamanho especificado é copiada no buffer e depois movida para o destino. Portanto, se a memória estiver sobreposta, não haverá efeitos colaterais.no caso de
memcpy()
, não há buffer extra usado para a memória de origem. A cópia é feita diretamente na memória para que, quando houver sobreposição de memória, obtenhamos resultados inesperados.Isso pode ser observado pelo seguinte código:
A saída é:
fonte
Como já apontado em outras respostas,
memmove
é mais sofisticado do quememcpy
tal, responsável por sobreposições de memória. O resultado do memmove é definido como se osrc
arquivo fosse copiado para um buffer e, em seguida, copiado para o bufferdst
. Isso NÃO significa que a implementação real usa qualquer buffer, mas provavelmente faz alguma aritmética de ponteiro.fonte
O compilador pode otimizar o memcpy, por exemplo:
Este memcpy pode ser otimizado como:
x = *(int*)some_pointer;
fonte
int
acessos desalinhados . Em algumas arquiteturas (por exemplo, Cortex-M0), a tentativa de buscar um de 32 bitsint
em um endereço que não seja múltiplo de quatro causará uma falha (masmemcpy
funcionaria). Se alguém estiver usando uma CPU que permita acesso não alinhado ou usando um compilador com uma palavra-chave que instrua o compilador a montar números inteiros a partir de bytes buscados separadamente, quando necessário, poderá fazer algo como#define UNALIGNED __unaligned
e, em seguida, `x = * (int UNALIGNED * ) some_pointer;char x = "12345"; int *i; i = *(int *)(x + 1);
mas alguns o fazem porque corrigem a cópia durante a falha. Eu trabalhei em um sistema como esse e demorou um pouco para entender por que o desempenho era tão ruim.*(int *)some_pointer
é uma estrita aliasing violação, mas você provavelmente significa que o compilador conjunto de saída que copia um intO código fornecido nos links http://clc-wiki.net/wiki/memcpy para memcpy parece me confundir um pouco, pois não fornece a mesma saída quando eu o implementei usando o exemplo abaixo.
Resultado :
Mas agora você pode entender por que o memmove cuidará de questões sobrepostas.
fonte
Esboço padrão C11
O rascunho padrão do C11 N1570 diz:
7.24.2.1 "A função memcpy":
7.24.2.2 "A função memmove":
Portanto, qualquer sobreposição
memcpy
leva a um comportamento indefinido, e tudo pode acontecer: ruim, nada ou até bom. Bom é raro :-)memmove
no entanto, diz claramente que tudo acontece como se um buffer intermediário fosse usado, portanto as sobreposições claramente são aceitáveis.std::copy
No entanto, o C ++ é mais tolerante e permite sobreposições: std :: copy lida com intervalos sobrepostos?fonte
memmove
use uma matriz temporária extra de n, então usa memória extra? Mas como pode se não tivermos acesso a nenhuma memória? (Está usando 2x a memória).Eu tentei executar o mesmo programa usando o eclipse e mostra uma clara diferença entre
memcpy
ememmove
.memcpy()
não se preocupa com a sobreposição do local da memória, o que resulta em corrupção de dados, enquantomemmove()
copia os dados primeiro para a variável temporária e depois copia para o local real da memória.Ao tentar copiar dados do local
str1
parastr1+2
, a saída dememcpy
é "aaaaaa
". A questão seria como?memcpy()
copiará um byte de cada vez da esquerda para a direita. Como mostrado no seu programa "aabbcc
", todas as cópias ocorrerão como abaixo,aabbcc -> aaabcc
aaabcc -> aaaacc
aaaacc -> aaaaac
aaaaac -> aaaaaa
memmove()
primeiro copia os dados para a variável temporária e depois copia para o local real da memória.aabbcc(actual) -> aabbcc(temp)
aabbcc(temp) -> aaabcc(act)
aabbcc(temp) -> aaaacc(act)
aabbcc(temp) -> aaaabc(act)
aabbcc(temp) -> aaaabb(act)
Saída é
memcpy
:aaaaaa
memmove
:aaaabb
fonte
memmove()
cópias para um local intermediário. Deve apenas copiar ao contrário, quando necessário.