HOWTO · Arduino

Arduino memcpy e memmove

Si può copiare un blocco di memoria da una variabile a un'altra usando le funzioni memcpy() e memmove().

In questa pagina

I programmi per Arduino possono usare le funzioni della libreria standard C memcpy() e memmove() per copiare byte tra posizioni di memoria. Scegli memcpy() quando l’area di origine e quella di destinazione non si sovrappongono; scegli memmove() quando potrebbero sovrapporsi. Entrambe le funzioni copiano il numero di byte specificato dal chiamante e nessuna conosce la dimensione del buffer di destinazione: spetta quindi al programma assicurarsi che lo spazio sia sufficiente.

Usare memcpy() per copiare byte

La firma della funzione è void *memcpy(void *dest, const void *src, size_t n);. L’argomento dest punta al primo byte da scrivere, src al primo byte da leggere e n indica un numero di byte, non di elementi dell’array. Per un array, sizeof(array) ne restituisce la dimensione totale in byte finché l’array è nello scope; per un puntatore, invece, sizeof(pointer) restituisce la dimensione del puntatore.

Le aree di origine e destinazione non devono sovrapporsi. Se anche un solo byte appartiene a entrambe, chiamare memcpy() ha un comportamento indefinito: le regole del C e del C++ non garantiscono alcun risultato particolare. Non è corretto affermare che la chiamata provocherà sempre un overflow o corromperà i dati in un modo specifico. Per una copia normale, usa un buffer di destinazione separato e assicurati che contenga almeno n byte scrivibili. La funzione non alloca memoria e non controlla la capacità del buffer.

Usare memmove() con aree sovrapposte

void *memmove(void *dest, const void *src, size_t n); accetta gli stessi argomenti e copia lo stesso numero di byte. A differenza di memcpy(), il suo comportamento è definito anche quando le aree di origine e destinazione si sovrappongono. Il risultato è come se i byte originali di origine fossero prima conservati e poi scritti nella destinazione; perciò la funzione è adatta a spostare una parte di un array all’interno dello stesso array.

Questa garanzia riguarda soltanto la sovrapposizione. memmove() scrive comunque n byte a partire da dest: non può determinare la dimensione dell’oggetto puntato né impedire una scrittura fuori dai limiti. Prima di chiamarla, controlla lo spazio disponibile nella destinazione e assicurati che l’area di origine contenga almeno n byte validi.

Scegliere tra memcpy() e memmove()

Usa memcpy() quando sai che le due aree sono separate. In questo caso esprime direttamente la scelta più semplice e può consentire all’implementazione di usare una copia ottimizzata per aree non sovrapposte. Usa memmove() ogni volta che la sovrapposizione è possibile o non è chiara la relazione tra le aree. Non fare affidamento su una piattaforma specifica che sembra gestire correttamente una chiamata a memcpy() con aree sovrapposte: le ottimizzazioni del compilatore possono rendere il comportamento indefinito particolarmente imprevedibile.

Con entrambe le funzioni, determina prima il numero di byte da copiare e la capacità della destinazione. Se copi un array intero, usa il suo valore sizeof mentre è ancora un array e dimensiona la destinazione per contenere almeno quel numero di byte. Se copi elementi di un tipo diverso da char, unsigned char o std::byte, moltiplica il numero di elementi per sizeof(element) e verifica che l’operazione sia adatta a quel tipo: queste funzioni copiano le rappresentazioni degli oggetti, non eseguono conversioni o assegnazioni elemento per elemento.

Esempio Arduino: copiare un array

Il seguente programma C++ autonomo mostra come copiare un array in un buffer separato e come spostare dati all’interno dello stesso buffer. Usa le stesse chiamate della libreria standard disponibili nel codice C++ per Arduino. Su una scheda, inserisci la copia in setup() e invia la destinazione al Monitor seriale con Serial.println(); dimensiona la destinazione in base ai byte copiati, compreso il carattere nullo finale di una stringa se intendi stamparla come stringa C.

Il codice e le eventuali etichette o gli output visibili nei blocchi riportati restano in inglese.

Esegui questo esempio C++ completo con un compilatore C++ standard:

#include <cstdio>
#include <cstring>

int main() {
    const char source[] = "LED";
    char destination[sizeof(source)] = {};
    std::memcpy(destination, source, sizeof(source));
    std::printf("%s\n", destination);

    char buffer[] = "ABCDE";
    std::memmove(buffer + 1, buffer, 4);
    std::printf("%s\n", buffer);
    return 0;
}
LED
AABCD

Qui, destination ha esattamente lo spazio necessario per l’array di origine, compreso il terminatore nullo. La seconda copia sposta di una posizione verso destra quattro byte all’interno di buffer; le aree di destinazione e di origine si sovrappongono, quindi memmove() è la funzione adatta. In uno sketch Arduino, includi normalmente Arduino.h e sostituisci le chiamate desktop a std::printf() con Serial.println() dopo Serial.begin(...). Le chiamate per copiare la memoria e i relativi requisiti di dimensionamento restano gli stessi.