Domanda

Qual è la differenza tra memmove e memcpy? Quale di solito usi e come?

È stato utile?

Soluzione

Con memcpy, la destinazione non può sovrapporsi affatto alla sorgente. Con memmove può. Ciò significa che <=> potrebbe essere leggermente più lento di <=>, poiché non può fare gli stessi presupposti.

Ad esempio, <=> potrebbe sempre copiare gli indirizzi dal più basso al più alto. Se la destinazione si sovrappone alla fonte, ciò significa che alcuni indirizzi verranno sovrascritti prima della copia. <=> rileverà questo e lo copierebbe nell'altra direzione - dall'alto verso il basso - in questo caso. Tuttavia, controllare questo e passare a un altro algoritmo (forse meno efficiente) richiede tempo.

Altri suggerimenti

memmove può gestire la memoria sovrapposta, memcpy impossibile.

Valuta

char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up

Ovviamente la sorgente e la destinazione ora si sovrappongono, stiamo sovrascrivendo quot &; &-bar quot; con " bar " ;. È un comportamento indefinito usando <=> se l'origine e la destinazione si sovrappongono, quindi in questo caso abbiamo bisogno di <=>.

memmove(&str[3],&str[4],4); //fine

Dalla memcpy pagina man.

  

La funzione memcpy () copia n byte   dall'area di memoria src all'area di memoria   dest. Le aree di memoria non dovrebbero   sovrapposizione. Usa memmove (3) se la memoria   le aree si sovrappongono.

La differenza principale tra memmove() e memcpy() è che in <=> viene utilizzato un buffer - memoria temporanea - quindi non vi è alcun rischio di sovrapposizione. D'altra parte, <=> copia direttamente i dati dalla posizione puntata dalla fonte nella posizione puntata dalla destinazione . ( http://www.cplusplus.com/reference/cstring/memcpy/ )

Considera i seguenti esempi:

  1. #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, questo verrà stampato:

    stackoverflow
    stackstacklow
    stackstacklow
    
  2. Ma 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;
    }
    

    Output:

    stackoverflow
    stackstackovw
    stackstackstw
    

È perché " memcpy () " fa quanto segue:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw

Uno gestisce destinazioni sovrapposte, l'altro no.

semplicemente dalla norma ISO / IEC: 9899 è ben descritta.

  

7.21.2.1 La funzione memcpy

     

[...]

     

2 La funzione memcpy copia n caratteri dall'oggetto puntato da s2 in   oggetto indicato da s1. Se la copia avviene tra oggetti che si sovrappongono, il comportamento non è definito.

E

  

7.21.2.2 La funzione memmove

     

[...]

     

2 La funzione memmove copia n caratteri dall'oggetto puntato da s2 in   oggetto indicato da s1. La copia avviene come se n caratteri dall'oggetto   a cui punta s2 vengono prima copiati in un array temporaneo di n caratteri che non lo fanno   si sovrappongono gli oggetti indicati da s1 e s2, quindi gli n caratteri da   gli array temporanei vengono copiati nell'oggetto indicato da s1.

Quale di solito utilizzo secondo la domanda, dipende da quale funzionalità ho bisogno.

Nel testo normale memcpy() non consente la sovrapposizione di s1 e s2, mentre memmove() lo fa.

Supponendo che dovresti implementare entrambi, l'implementazione potrebbe apparire così:

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 piuttosto spiegare la differenza. memmove copia sempre in modo tale che sia ancora sicuro se src e dst si sovrappongono, mentre memcpy non importa come dice la documentazione quando si utilizza <=>, le due aree di memoria non devono sovrapporsi.

es. se <=> copia " fronte retro " e i blocchi di memoria sono allineati come questo

[---- src ----]
            [---- dst ---]

la copia del primo byte da <=> a <=> distrugge già il contenuto degli ultimi byte di <=> prima che questi siano stati copiati. Copia solo & Quot; torna all'inizio & Quot; porterà a risultati corretti.

Ora scambia <=> e <=>:

[---- dst ----]
            [---- src ---]

In quel caso è sicuro copiare " fronte-retro " come copia " torna all'inizio " distruggerebbe <=> già vicino alla sua facciata quando si copia il primo byte.

Potresti aver notato che l'implementazione <=> sopra non verifica nemmeno se effettivamente si sovrappongono, controlla solo le loro posizioni relative, ma solo questo renderà la copia sicura. Dato che <=> di solito usa il modo più veloce possibile per copiare la memoria su qualsiasi sistema, <=> è di solito 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 <=> copia sempre " fronte-retro " oppure " torna all'inizio " ;, <=> può anche utilizzare <=> in uno dei casi sovrapposti ma <=> può persino copiare in modo diverso a seconda di come sono allineati i dati e / o quanti dati devono essere copiati, quindi anche se hai testato come <=> copie sul tuo sistema, non puoi fare affidamento sul risultato del test per essere sempre corretti.

Cosa significa questo per te quando decidi quale chiamare?

  1. A meno che tu non sappia per certo che <=> e <=> non si sovrappongono, chiama <=> in quanto porterà sempre a risultati corretti ed è generalmente più veloce possibile per il caso di copia richiedono.

  2. Se sai con certezza che <=> e <=> non si sovrappongono, chiama <=> poiché non importa quale si chiama per il risultato, entrambi funzioneranno correttamente in quel caso, ma <=> non sarà mai più veloce di <=> e se sei sfortunato, potrebbe anche essere più lento, quindi puoi solo vincere chiamando <=>.

Esistono due modi ovvi per implementare mempcpy(void *dest, const void *src, size_t n) (ignorando il valore restituito):

  1. for (char *p=src, *q=dest;  n-->0;  ++p, ++q)
        *q=*p;
    
  2. char *p=src, *q=dest;
    while (n-->0)
        q[n]=p[n];
    

Nella prima implementazione, la copia procede dagli indirizzi più bassi a quelli più alti e, nella seconda, da quelli più alti a quelli più bassi. Se l'intervallo da copiare si sovrappone (come nel caso dello scrolling di un framebuffer, ad esempio), solo una direzione dell'operazione è corretta e l'altra sovrascriverà le posizioni che verranno successivamente lette.

Un'implementazione memmove(), nella sua forma più semplice, testerà dest<src (in qualche modo dipendente dalla piattaforma) ed eseguirà la direzione appropriata di memcpy().

Il codice utente non può farlo ovviamente, perché anche dopo aver lanciato src e dst su un tipo di puntatore concreto, non puntano (in generale) sullo stesso oggetto e quindi non possono essere confrontati . Ma la libreria standard può avere abbastanza conoscenza della piattaforma per eseguire un simile 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 aree di origine e destinazione sovrapposte, mentre memcpy no. Tra i due, memcpy è molto più efficiente. Quindi, meglio UTILIZZARLO memcpy se puoi.

Riferimento: https://www.youtube.com/watch?v=Yr1YnOVG -4g Dr. Jerry Cain, (Stanford Intro Systems Lecture - 7) Tempo: 36:00

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top