HOWTO · Arduino
Arduino memcpy und memmove
Mit den Funktionen memcpy() und memmove() kann ein Speicherblock von einer Variablen in eine andere kopiert werden.
Auf dieser Seite
Arduino-Programme können die C-Standardbibliotheksfunktionen memcpy() und memmove() verwenden, um Bytes zwischen Speicherstellen zu kopieren. Verwenden Sie memcpy(), wenn sich Quell- und Zielbereich nicht überschneiden, und memmove(), wenn eine Überschneidung möglich ist. Beide Funktionen kopieren eine vorgegebene Anzahl von Bytes. Da keine der beiden die Größe des Zielpuffers kennt, muss das Programm sicherstellen, dass dort genügend Platz vorhanden ist.
Mit memcpy() Bytes kopieren
Die Funktionssignatur lautet void *memcpy(void *dest, const void *src, size_t n);. Das Argument dest zeigt auf das erste zu beschreibende Byte, src auf das erste zu lesende Byte, und n gibt die Anzahl der Bytes an. Es handelt sich nicht um die Anzahl der Array-Elemente. Bei einem Array liefert sizeof(array) seine Gesamtgröße in Bytes, solange es sich noch im Gültigkeitsbereich befindet. Bei einem Zeiger ergibt sizeof(pointer) dagegen die Größe des Zeigers.
Quell- und Zielbereich dürfen sich nicht überschneiden. Gehört auch nur ein Byte zu beiden Bereichen, hat ein Aufruf von memcpy() undefiniertes Verhalten: Die Sprachregeln von C und C++ garantieren dann kein bestimmtes Ergebnis. Es wäre falsch vorherzusagen, dass der Aufruf immer einen Überlauf oder eine bestimmte Datenbeschädigung verursacht. Verwenden Sie für eine gewöhnliche Kopie einen separaten Zielpuffer und stellen Sie sicher, dass er mindestens n beschreibbare Bytes aufnehmen kann. Die Funktion reserviert keinen Speicher und prüft nicht, ob genügend Platz vorhanden ist.
Mit memmove() überlappende Bereiche kopieren
void *memmove(void *dest, const void *src, size_t n); erwartet dieselben Argumente und kopiert dieselbe Anzahl an Bytes. Anders als bei memcpy() ist das Verhalten definiert, wenn sich Quell- und Zielbereich überschneiden. Das Ergebnis entspricht einer Ausführung, bei der die ursprünglichen Quellbytes zunächst gesichert und anschließend an das Ziel geschrieben werden. Daher eignet sich die Funktion beispielsweise, um einen Teil eines Arrays innerhalb desselben Arrays zu verschieben.
Diese Garantie betrifft ausschließlich überlappende Bereiche. memmove() schreibt weiterhin n Bytes ab der Adresse dest; die Funktion kann weder die Größe des Objekts hinter einem Zeiger bestimmen noch einen Schreibzugriff außerhalb des verfügbaren Speicherbereichs verhindern. Prüfen Sie vor dem Aufruf den verfügbaren Platz im Zielbereich und stellen Sie sicher, dass der Quellbereich für n Bytes gültig ist.
Zwischen memcpy() und memmove() wählen
Verwenden Sie memcpy(), wenn sicher ist, dass die beiden Bereiche getrennt sind. So drücken Sie den einfacheren Fall direkt aus; außerdem kann die Implementierung möglicherweise eine optimierte Kopie für nicht überlappende Bereiche verwenden. Nutzen Sie memmove(), sobald eine Überschneidung möglich oder das Verhältnis der Bereiche unklar ist. Verlassen Sie sich nicht darauf, dass ein bestimmtes System einen Aufruf von memcpy() mit überlappenden Bereichen scheinbar korrekt ausführt. Compileroptimierungen können undefiniertes Verhalten besonders unvorhersehbar machen.
Bestimmen Sie bei beiden Funktionen zuerst die Byteanzahl und die Kapazität des Zielbereichs. Wenn Sie ein ganzes Array kopieren, verwenden Sie seinen sizeof-Wert, solange es noch ein Array ist, und dimensionieren Sie das Ziel für mindestens so viele Bytes. Beim Kopieren von Elementen eines anderen Typs als char, unsigned char oder std::byte multiplizieren Sie die Elementanzahl mit sizeof(element) und prüfen Sie, ob der Vorgang für diesen Typ geeignet ist. Diese Funktionen kopieren Objektdarstellungen; sie wandeln Elemente nicht einzeln um und weisen sie auch nicht einzeln zu.
Arduino-Beispiel: ein Array kopieren
Das folgende eigenständige C++-Programm zeigt eine Array-Kopie in einen separaten Puffer und das Verschieben von Daten innerhalb eines Puffers. Es verwendet dieselben Standardbibliotheksaufrufe, die auch in C++-Code für Arduino verfügbar sind. Auf einem Board führen Sie die Kopie in setup() aus und senden das Ziel mit Serial.println() an den Serial Monitor. Dimensionieren Sie den Zielpuffer für alle kopierten Bytes; wenn Sie ihn als C-String ausgeben möchten, muss auch das abschließende Nullbyte Platz haben.
Führen Sie dieses vollständige C++-Beispiel mit einem Standard-C++-Compiler aus. Die im unveränderten Beispiel ausgegebenen Zeichenfolgen und Bezeichnungen bleiben Englisch: Zuerst wird „LED“, danach „AABCD“ ausgegeben.
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
#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
Hier ist destination genau groß genug für das Quellarray einschließlich des abschließenden Nullbytes. Die zweite Kopie verschiebt vier Bytes innerhalb von buffer um eine Position nach rechts. Quell- und Zielbereich überschneiden sich deshalb, sodass memmove() die passende Funktion ist. Fügen Sie in einem Arduino-Sketch wie üblich Arduino.h ein und ersetzen Sie die Desktop-Aufrufe von std::printf() nach Serial.begin(...) durch Serial.println(). Die Speicherkopieraufrufe und ihre Anforderungen an die Puffergröße bleiben gleich.