HOWTO · Arduino
Arduino strcpy Funktion
Mit der Funktion strcpy() in Arduino kann man einen String von einer Variablen in eine andere kopieren.
Auf dieser Seite
Mit strcpy(destination, source) kopierst du eine nullterminierte C-Zeichenkette einschließlich ihres abschließenden \0. Das Ziel muss Platz für jedes Zeichen der Quelle und den Terminator bieten. Beispielsweise benötigt "Hello World" 12 Byte. Die Funktion arbeitet mit Zeichenarrays, nicht mit Arduino-String-Objekten. Verwende sie nicht, wenn sich Quelle und Ziel überlappen.
strcpy() und nullterminierte Zeichenketten verstehen
Eine C-Zeichenkette ist eine Folge von Zeichen, auf die ein Nullbyte (\0) folgt. strcpy() erwartet einen Zeiger auf ein beschreibbares Ziel und einen Quellzeiger auf eine solche Zeichenkette. Die Funktion kopiert bis einschließlich des Terminators und gibt den Zielzeiger zurück. Die Quelle darf als const char * deklariert sein; das Ziel muss beschreibbar sein.
Die Funktion erhält keine Angabe zur Zielkapazität und kann daher nicht prüfen, ob das Array groß genug ist. Reicht der Platz nicht für alle Zeichen und den Terminator, ist das Verhalten des Programms undefiniert. Dass ein Lauf scheinbar funktioniert, macht eine Kopie in einen zu kleinen Puffer nicht sicher. Unterscheide außerdem eine C-Zeichenkette wie char name[] von einem Arduino-String-Objekt: Dafür gelten unterschiedliche Schnittstellen.
Einen String sicher mit strcpy() kopieren
Dieses vollständige Sketch kopiert die 11 Zeichen aus "Hello World" samt abschließendem \0 in ein 12 Byte großes Array und gibt die kopierte Zeichenkette anschließend über Serial aus. Der unten gezeigte Serial-Ersatz für den Host macht dieselbe setup()-Logik zu einem eigenständig ausführbaren Beispiel. Auf einem Arduino-Board stellt der Core Serial bereit und ruft setup() auf.
#include <cstring>
#ifndef ARDUINO
#include <iostream>
struct SerialPort {
void begin(unsigned long) {}
void println(const char* value) { std::cout << value << '\n'; }
} Serial;
#endif
const char source[] = "Hello World";
char destination[sizeof(source)]; // 12 bytes: 11 characters plus \0
void setup() {
Serial.begin(9600);
std::strcpy(destination, source);
Serial.println(destination);
}
void loop() {
// The copy is performed once in setup().
}
#ifndef ARDUINO
int main() {
setup();
return 0;
}
#endif
Hello World
Bei dieser fest definierten Quelle enthält sizeof(source) auch den Terminator; deshalb beträgt die Zielkapazität 12 Byte. Bei einem Zeiger auf Text liefert sizeof(pointer) dagegen nur die Zeigergröße. Berechne die benötigte Kapazität in diesem Fall aus der Zeichenkettenlänge plus eins. Quelle und Ziel müssen getrennte, nicht überlappende Speicherbereiche sein. Das folgende C-Beispiel für den Host zeigt dieselbe Kopie einer Zeichenkette bekannter Länge in der konfigurierten Laufzeit.
#include <stdio.h>
#include <string.h>
int main(void) {
const char source[] = "Hello World";
char destination[sizeof(source)];
strcpy(destination, source);
puts(destination);
return 0;
}
Hello World
strncpy() und strlcpy() vergleichen
strncpy(destination, source, count) kopiert höchstens count Byte. Ist die Quelle kürzer als count, füllt die Funktion die übrigen Zielbytes mit Nullbytes auf. Ist die Quelle mindestens count Byte lang, fügt sie keinen Terminator an. Ein Zielarray, das durch strncpy() bis zu seiner Kapazität gefüllt wurde, darf daher erst dann an Zeichenkettenfunktionen übergeben werden, wenn die Terminierung sichergestellt ist. Bei einem Zielarray mit einer Kapazität größer null ist ein gängiges Vorgehen, höchstens capacity - 1 Byte zu kopieren und anschließend destination[capacity - 1] = '\0' zu setzen. So entsteht eine terminierte, möglicherweise gekürzte Zeichenkette. Das prüft jedoch nicht die Quelle und verhindert auch keine anderen Speicherfehler.
strlcpy(destination, source, size) erhält die gesamte Zielkapazität einschließlich des Platzes für \0. Bei size größer null terminiert die Funktion das Ziel. Bei Bedarf kürzt sie die Zeichenkette und gibt die Länge der vollständigen Quelle zurück. Durch den Vergleich mit size lässt sich eine Kürzung erkennen. strlcpy() ist nicht überall verfügbar: Prüfe vor der Verwendung, ob der ausgewählte Arduino-Core und die Toolchain die Funktion bereitstellen. Andernfalls verwende eine unterstützte Funktion oder eine sorgfältig dimensionierte Kopierstrategie.
Serielle Eingaben in ein Zeichenarray kopieren
Dieses Sketch liest höchstens 31 Byte in ein 32 Byte großes Eingabearray, setzt am zurückgegebenen Bytezähler einen Terminator, kopiert die resultierende C-Zeichenkette in ein separates Zielarray mit 32 Byte und gibt sie aus. Auf einem Board verwendet es die Arduino-API Serial. Im unten gezeigten Host-Lauf liefert ein kleiner Ersatz die Beispielzeile Hello World, sodass sich die Kopierlogik ohne Hardware ausführen lässt.
#include <cstddef>
#include <cstring>
#ifndef ARDUINO
#include <iostream>
struct SerialPort {
void begin(unsigned long) {}
void setTimeout(unsigned long) {}
void println(const char* value) { std::cout << value << '\n'; }
std::size_t readBytesUntil(char delimiter, char* buffer, std::size_t capacity) {
const char sample[] = "Hello World";
std::size_t count = 0;
while (count < capacity && sample[count] != '\0' && sample[count] != delimiter) {
buffer[count] = sample[count];
++count;
}
return count;
}
} Serial;
#endif
const std::size_t INPUT_CAPACITY = 32;
char inputBuffer[INPUT_CAPACITY];
char destination[INPUT_CAPACITY];
void setup() {
Serial.begin(9600);
Serial.setTimeout(1000);
Serial.println("Enter a line:");
std::size_t bytesRead = Serial.readBytesUntil('\n', inputBuffer, INPUT_CAPACITY - 1);
inputBuffer[bytesRead] = '\0';
std::strcpy(destination, inputBuffer);
Serial.println(destination);
}
void loop() {
// This one-shot input example runs once in setup().
}
#ifndef ARDUINO
int main() {
setup();
return 0;
}
#endif
Enter a line:
Hello World
readBytesUntil() endet, wenn die Funktion \n liest, das Timeout von 1000 ms abläuft oder 31 Byte eingelesen wurden. Der zurückgegebene Wert vom Typ size_t beträgt höchstens 31. Daher bleibt inputBuffer[bytesRead] = '\0' innerhalb des 32 Byte großen Arrays. Das Ziel hat dieselbe Kapazität und kann somit die kopierten Bytes samt Terminator aufnehmen. Erreicht eine Zeile die Grenze von 31 Byte vor dem Zeilenumbruch, kann der Rest dieser Zeile für einen späteren Lesevorgang verfügbar bleiben. Auch ein Timeout kann eine unvollständige Zeile zurückgeben. Der Zeilenumbruch wird beim Auffinden verbraucht; ein vorangestelltes \r entfernt dieser Code jedoch nicht. Es kann daher am Ende der ausgegebenen Zeichenkette stehen. Entferne es ausdrücklich, wenn die Anwendung dieses Verhalten nicht benötigt.
Kopierfunktion auswählen und Pufferkapazität prüfen
Prüfe vor dem Kopieren, ob der Wert ein Arduino-String oder ein nullterminiertes Zeichenarray ist. Berechne bei C-Zeichenketten die Kapazität in Byte und berücksichtige dabei ein zusätzliches Byte für \0. Bei UTF-8-Text können sich Zeichenanzahl und Byteanzahl unterscheiden. Verwende strcpy() nur, wenn die vollständige Quelllänge sicher in das Ziel passt. Verwende strncpy() nur mit einem klaren Plan für Terminierung und Auffüllung. Alternativ kannst du strlcpy() nutzen, sofern die Funktion auf dem Ziel verfügbar ist; prüfe ihren Rückgabewert auf Kürzung. Begrenze bei Serial-Daten das Einlesen auf die Kapazität des Eingabearrays, setze den Terminator an den zurückgegebenen Bytezähler und dimensioniere jedes nachfolgende Ziel passend zur resultierenden Zeichenkette.