Com memcpy, o destino não pode sobrepor a fonte. Com memmoveele pode. Isso significa que memmovepode ser um pouco mais lento do que memcpy, pois não pode fazer as mesmas suposições.
Por exemplo, memcpysempre 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. memmovedetectaria 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.
ao usar o memcpy, como posso garantir que os endereços src e dest não se sobreponham? Pessoalmente, devo garantir que src e dest não se sobreponham?
Alcott
6
@ Alcott, não use o memcpy se você não souber que eles não se sobrepõem - use o memmove. Quando não há sobreposição, memmove e memcpy são equivalentes (embora o memcpy possa ser muito, muito, muito ligeiramente mais rápido).
bdonlan
Você pode usar a palavra-chave 'restringir' se estiver trabalhando com matrizes longas e quiser proteger seu processo de cópia. Por exemplo, se o método utiliza como parâmetros matrizes de entrada e saída e você deve verificar se o usuário não passa o mesmo endereço que a entrada e a saída. Leia mais aqui stackoverflow.com/questions/776283/…
DanielHsH 1/01/15
10
@DanielHsH 'restringir' é uma promessa que você faz ao compilador; não é imposto pelo compilador. Se você colocar 'restringir' nos seus argumentos e, de fato, tiver sobreposição (ou, geralmente, acessar os dados restritos a partir de ponteiros derivados de vários locais), o comportamento do programa é indefinido, erros estranhos ocorrerão e o compilador normalmente não irá avisá-lo sobre isso.
bdonlan
@dondonlan Não é apenas uma promessa para o compilador, é um requisito para o chamador. É um requisito que não é imposto, mas se você violar um requisito, não poderá reclamar se obtiver resultados inesperados. Violar um requisito é um comportamento indefinido, assim como 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.
Mecki
33
memmovepode lidar com sobreposição de memória, memcpynã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 memcpyse a origem e o destino se sobrepõem; nesse caso, precisamos memmove.
@ultraman: Porque PODE ter sido implementado usando assembley de baixo nível que requer que a memória não se sobreponha. Nesse caso, você poderia, por exemplo, gerar uma exceção de sinal ou hardware para o processador que interrompe o aplicativo. A documentação especifica que ela não lida com a condição, mas o padrão não especifica o que acontecerá quando essas condições forem submetidas a nova verificação (isso é conhecido como comportamento indefinido). Comportamento indefinido pode fazer qualquer coisa.
297 Martin Martin York
com o gcc 4.8.2, Até o memcpy também aceita ponteiros de origem e destino sobrepostos e funciona bem.
GeekyJ
4
@jagsgediya Claro que pode. Mas como o memcpy está documentado para não suportar isso, você não deve confiar nesse comportamento específico da implementação, é por isso que o memmove () existe. Pode ser diferente em outra versão do gcc. Pode ser diferente se o gcc inline o memcpy em vez de chamar memcpy () na glibc, pode ser diferente em uma versão mais antiga ou mais recente do glibc e assim por diante.
Nos
Da prática, parece que o memcpy e o memmove fizeram a mesma coisa. Um comportamento indefinido tão profundo.
A função memcpy () copia n bytes da área de memória src para a área de memória dest. As áreas de memória não devem se sobrepor. Use memmove (3) se as áreas de memória se sobreporem.
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);return0;}
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);return0;}
Mas, parece que a saída que você mencionou está invertida !!
kumar
1
Quando eu executar o mesmo programa, eu recebo o seguinte resultado: stackoverflow meios stackstackstw stackstackstw // Não há nenhuma diferença na saída entre memcpy e memmove
kumar
4
"é que em" memmove () ", um buffer - uma memória temporária - é usado;" Não é verdade. diz "como se", então apenas tem que se comportar assim, não que tenha que ser assim. Isso é realmente relevante, pois a maioria das implementações do memmove apenas faz uma troca de XOR.
dhein
2
Eu não acho que a implementação de 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).
Toby Speight
12
Supondo que você teria que implementar os dois, a implementação poderia ser assim:
void memmove (void* dst,constvoid* src,size_t count ){if((uintptr_t)src <(uintptr_t)dst){// Copy from back to front}elseif((uintptr_t)dst <(uintptr_t)src){// Copy from front to back}}void mempy (void* dst,constvoid* 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. memmovesempre copia de tal maneira, que ainda é seguro se srce se dstsobrepõe, embora memcpyapenas 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 memcpycópias "da frente para trás" e os blocos de memória estiverem alinhados como este
[---- src ----][---- dst ---]
copiando o primeiro byte srcpara dstjá destrói o conteúdo dos últimos bytes de srcantes que estes tenham sido copiadas. Apenas copiar "de trás para frente" levará a resultados corretos.
Agora troque srce dst:
[---- dst ----][---- src ---]
Nesse caso, só é seguro copiar "frente para trás", pois a cópia "de trás para frente" já destruiria srcperto de sua frente ao copiar o primeiro byte.
Você deve ter notado que a memmoveimplementaçã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 memcpygeralmente usa a maneira mais rápida possível de copiar memória em qualquer sistema, memmovegeralmente é implementado como:
void memmove (void* dst,constvoid* src,size_t count ){if((uintptr_t)src <(uintptr_t)dst
&&(uintptr_t)src + count >(uintptr_t)dst
){// Copy from back to front}elseif((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 memcpysempre copia "frente para trás" ou "volta para frente", memmovetambém pode ser usado memcpyem um dos casos sobrepostos, mas memcpypode 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 memcpycó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 srce dstnã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 srce dstnão se sobrepõe, ligue memcpy, pois não importa para quem você deseja o resultado, ambos funcionarão corretamente nesse caso, mas memmovenunca serão mais rápidos do que memcpye 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 s1e s2se 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 srce dstde 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.
Essa resposta diz "possivelmente um pouco mais rápido" e fornece dados quantitativos indicando apenas uma pequena diferença. Esta resposta afirma que um é "muito mais eficiente". Quanto mais eficiente você achou o mais rápido? BTW: Suponho que você queira dizer memcpy()e não memcopy().
chux - Restabelece Monica
O comentário é feito com base na palestra do Dr. Jerry Cain. Eu pediria que você ouvisse sua palestra às 36:00, apenas 2-3 minutos serão suficientes. E obrigado pela captura. : D
Respostas:
Com
memcpy
, o destino não pode sobrepor a fonte. Commemmove
ele pode. Isso significa quememmove
pode ser um pouco mais lento do quememcpy
, 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.fonte
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
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, precisamosmemmove
.fonte
Na página de manual do memcpy .
fonte
A principal diferença entre
memmove()
ememcpy()
é que emmemmove()
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:
Como você esperava, isso será impresso:
Mas neste exemplo, os resultados não serão os mesmos:
Resultado:
Isso ocorre porque "memcpy ()" faz o seguinte:
fonte
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:
E isso deve muito bem explicar a diferença.
memmove
sempre copia de tal maneira, que ainda é seguro sesrc
e sedst
sobrepõe, emboramemcpy
apenas não importe como a documentação diz ao usarmemcpy
, 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 estecopiando o primeiro byte
src
paradst
já destrói o conteúdo dos últimos bytes desrc
antes que estes tenham sido copiadas. Apenas copiar "de trás para frente" levará a resultados corretos.Agora troque
src
edst
: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. Comomemcpy
geralmente usa a maneira mais rápida possível de copiar memória em qualquer sistema,memmove
geralmente é implementado como:Às vezes, se
memcpy
sempre copia "frente para trás" ou "volta para frente",memmove
também pode ser usadomemcpy
em um dos casos sobrepostos, masmemcpy
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 asmemcpy
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
edst
não se sobreponha, liguememmove
, 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
edst
não se sobrepõe, liguememcpy
, pois não importa para quem você deseja o resultado, ambos funcionarão corretamente nesse caso, masmemmove
nunca serão mais rápidos do quememcpy
e se você tiver azar, pode até seja mais lento, para que você possa ganhar apenas chamadasmemcpy
.fonte
Um deles lida com destinos sobrepostos e o outro não.
fonte
simplesmente a partir da norma ISO / IEC: 9899, está bem descrita.
E
Qual deles eu costumo usar de acordo com a pergunta depende de qual funcionalidade eu preciso.
Em texto sem formatação
memcpy()
, não permites1
es2
se sobrepõe, enquantomemmove()
faz.fonte
Existem duas maneiras óbvias de implementar
mempcpy(void *dest, const void *src, size_t n)
(ignorando o valor de retorno):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 dememcpy()
.O código do usuário não pode fazer isso, é claro, porque mesmo depois da conversão
src
edst
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.
fonte
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
fonte
memcpy()
e nãomemcopy()
.