Risposte:
Con memcpy, la destinazione non può affatto sovrapporsi alla sorgente. Con memmoveesso può. Ciò significa che memmovepotrebbe essere leggermente più lento di memcpy, in quanto non può fare le stesse ipotesi.
Ad esempio, memcpypotrebbe sempre copiare gli indirizzi da basso ad alto. Se la destinazione si sovrappone alla sorgente, significa che alcuni indirizzi verranno sovrascritti prima di essere copiati. memmovelo rileverebbe e copierebbe nell'altra direzione, dall'alto verso il basso, in questo caso. Tuttavia, controllarlo e passare a un altro algoritmo (possibilmente meno efficiente) richiede tempo.
i = i++ + 1è undefined; il compilatore non ti vieta di scrivere esattamente quel codice ma il risultato di quell'istruzione può essere qualsiasi cosa e diversi compilatori o CPU mostreranno valori diversi qui.
memmovepuò gestire la sovrapposizione della memoria, memcpyno.
Tener conto di
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Ovviamente l'origine e la destinazione ora si sovrappongono, sovrascriviamo "-bar" con "bar". È un comportamento indefinito memcpyse l'origine e la destinazione si sovrappongono, quindi in questo caso abbiamo bisogno di memmove.
memmove(&str[3],&str[4],4); //fine
La differenza principale tra memmove()e memcpy()è che in memmove()un buffer viene utilizzata la memoria temporanea, quindi non vi è alcun rischio di sovrapposizione. D'altra parte, memcpy()copia direttamente i dati dalla posizione puntata dalla sorgente alla posizione puntata dalla destinazione . ( http://www.cplusplus.com/reference/cstring/memcpy/ )
Considera i seguenti esempi:
#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;
}
Come previsto, verrà stampato:
stackoverflow
stackstacklow
stackstacklowMa in questo esempio, i risultati non saranno gli stessi:
#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;
}
Produzione:
stackoverflow
stackstackovw
stackstackstwÈ perché "memcpy ()" fa quanto segue:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
memmove()sia richiesta per utilizzare un buffer. È perfettamente autorizzato a spostarsi sul posto (a condizione che ogni lettura venga completata prima di qualsiasi scrittura allo stesso indirizzo).
Supponendo che dovresti implementare entrambi, l'implementazione potrebbe essere simile a quella:
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 questo dovrebbe spiegare abbastanza bene la differenza. memmovecopia sempre in modo tale che sia comunque sicuro se srce si dstsovrappongono, mentre memcpyproprio non interessa come dice la documentazione quando si usa memcpy, le due aree di memoria non devono sovrapporsi.
Ad esempio, se le memcpycopie "dalla parte anteriore a quella posteriore" ei blocchi di memoria sono allineati in questo modo
[---- src ----]
[---- dst ---]
la copia del primo byte di srcto dstdistrugge già il contenuto degli ultimi byte di srcprima che questi siano stati copiati. Solo la copia "dal retro al davanti" porterà a risultati corretti.
Ora scambia srce dst:
[---- dst ----]
[---- src ---]
In questo caso è sicuro copiare solo "da davanti a dietro" poiché la copia "da dietro a davanti" distruggerebbe srcla parte anteriore già quando si copia il primo byte.
Potresti aver notato che l' memmoveimplementazione sopra non verifica nemmeno se effettivamente si sovrappongono, controlla solo le loro posizioni relative, ma questo da solo renderà la copia sicura. Poiché di memcpysolito utilizza il modo più veloce possibile per copiare la memoria su qualsiasi sistema, di memmovesolito è piuttosto implementato come:
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);
}
}
A volte, se memcpycopia sempre "da davanti a dietro" o "da dietro a davanti", memmovepuò anche essere utilizzato memcpyin uno dei casi sovrapposti, ma memcpypuò anche copiare in un modo diverso a seconda di come i dati sono allineati e / o di quanti dati devono essere copiato, quindi anche se hai testato come memcpycopia sul tuo sistema, non puoi fare affidamento sul risultato del test per essere sempre corretto.
Cosa significa per te quando decidi quale chiamare?
A meno che tu non lo sappia per certo srce dstnon si sovrapponga, chiama memmovein quanto porterà sempre a risultati corretti ed è solitamente il più veloce possibile per il caso di copia richiesto.
Se lo sai per certo srce dstnon si sovrappongono, chiama memcpyperché non importa quale chiami per il risultato, entrambi funzioneranno correttamente in quel caso, ma memmovenon saranno mai più veloci di memcpye se sei sfortunato, potrebbe anche sii più lento, così puoi vincere solo chiamate memcpy.
semplicemente dallo standard ISO / IEC: 9899 è ben descritto.
7.21.2.1 La funzione memcpy
[...]
2 La funzione memcpy copia n caratteri dall'oggetto puntato da s2 nell'oggetto puntato da s1. Se la copia avviene tra oggetti che si sovrappongono, il comportamento è indefinito.
E
7.21.2.2 La funzione memmove
[...]
2 La funzione memmove copia n caratteri dall'oggetto puntato da s2 nell'oggetto puntato da s1. La copia avviene come se gli n caratteri dell'oggetto puntato da s2 venissero prima copiati in un array temporaneo di n caratteri che non si sovrappone agli oggetti puntati da s1 e s2, quindi gli n caratteri dall'array temporaneo vengono copiati in l'oggetto puntato da s1.
Quale utilizzo di solito in base alla domanda, dipende dalla funzionalità di cui ho bisogno.
In testo normale memcpy()non consente s1e s2di sovrapporsi, mentre lo memmove()fa.
Esistono due modi ovvi per implementare mempcpy(void *dest, const void *src, size_t n)(ignorando il valore restituito):
for (char *p=src, *q=dest; n-->0; ++p, ++q)
*q=*p;char *p=src, *q=dest;
while (n-->0)
q[n]=p[n];Nella prima implementazione, la copia procede da indirizzi bassi a indirizzi alti e nella seconda da indirizzi alti a bassi. Se l'intervallo da copiare si sovrappone (come nel caso dello scorrimento di un framebuffer, ad esempio), solo una direzione di funzionamento è corretta e l'altra sovrascriverà le posizioni che verranno successivamente lette.
Un memmove()attuazione, nel modo più semplice, verificherà dest<src(in qualche modo dipendente dalla piattaforma), ed eseguire il corrispondente senso di memcpy().
Il codice utente non può farlo, naturalmente, perché anche dopo la fusione srce dstad un certo tipo di puntatore concreto, non lo fanno (in generale) punto nello stesso oggetto e quindi non possono essere confrontati. Ma la libreria standard può avere una conoscenza della piattaforma sufficiente per eseguire tale confronto senza causare un comportamento indefinito.
Si noti che nella vita reale, le implementazioni tendono ad essere significativamente più complesse, per ottenere le massime prestazioni da trasferimenti più grandi (quando l'allineamento lo consente) e / o un buon utilizzo della cache dei dati. Il codice sopra è solo per rendere il punto il più semplice possibile.
memmove può gestire la sovrapposizione di regioni di origine e destinazione, mentre memcpy no. Tra i due, memcpy è molto più efficiente. Quindi, meglio usare memcpy se puoi.
Riferimento: https://www.youtube.com/watch?v=Yr1YnOVG-4g Dr. Jerry Cain, (Stanford Intro Sistemi Lecture - 7) Ora: 36:00
memcpy()e non memcopy().