Von mir verwendete C-Funktionen und C-Funktionalitäten in Arduino
Da ich nicht mit der Programmiersprache C vorbelastet bin, wollte ich ursprünglich meine Programme rein in Arduino, also ohne zusätzliche C-Funktionen und -Funktionalitäten, programmieren.
Bald habe ich allerdings erkannt, dass es doch ab und zu ganz nützlich sein kann, meinen Vorsatz zu brechen und auf C-Funktionen und -Funktionalitäten zurück zu greifen, wenn dadurch die Programmierung einfacher wird. Vor allem bei der Bearbeitung von Zeichenketten nütze ich vermehrt die C-Funktionen, auch wenn Arduino mit seiner "String class" recht umfangreiche Funktionen bei der String-Bearbeitung zur Verfügung gestellt hat. Manchmal kommt man aber eben mit Stringfunktionen in C besser zum Ziel, vor allem wenn man bedenkt, dass es bei der Verwendung der "String class" zur Speicherfragmentierung kommt, die im schlimmsten Fall zum Programmabsturz führen kann.
Zur Dokumentation stelle ich daher nach und nach die von mir verwendeten C-Funktionen mit ihren Prototypen und sonstige Funktionalitäten, jeweils mit einem kleinen Beispiel, hier ein. Es soll für mich zum schnellen Auffinden von Funktionen und Funktionalitäten dienen, die ich in meinen Programmen schon einmal eingesetzt habe, deren Anwendung oder Aufruf mir aber gerade entfallen ist. So gehe ich davon aus, dass die nachfolgende Auflistung weiter anwachsen wird.
Meine Informationen über die von mir eingesetzten C-Funktionen und C-Funktionalitäten habe ich, wenn nicht anders angegeben, von hier: www.cplusplus.com/
Info: In nachfolgenden Prototypen der Funktionen kommt immer wieder der Datentyp size_t vor. Das ist ein Alias (= Pseudonym oder Deckname) für die fundamentalen unsigned Interger-Datentypen, wie byte, unsigned Int, unsigned long, etc.
Funktionen zur Stringbearbeitung
(Verketten, vergleichen, kopieren, zerteilen , löschen, ...)
- strcat
- strncat
- strcmp
- strcpy
- strncpy
- strlen
- strstr
- strtok
und
- String löschen
Mit strcat (stringcatenate) Strings verketten:
char* strcat (char* destination, const char* source);
Funktion: Verbindet source mit destination, d.h. source wird an destination angehängt (verkettet)
char text1[10] = "12345678";
char text2[10] = "absdefgh";
strcat(text1, text2);
Mit strncat (string-n-catenate) einen String um n-Zeichen erweitern:
char* strncat (char* destination, const char* source, size_t n);
Funktion: Verbindet die ersten n-Zeichen von source mit destination, d.h. die ersten n-Zeichen von source werden an destination angehängt (verkettet)
Ein einzelnes Zeichen anhängen:
char text1[10] = "abc";
char c = 'x';
strncat(text1, &c, 1);
Die ersten 2 Zeichen eines Character-Arrays anhängen:
char text1[10] = "abcd";
char text2[] = "ABCD";strncat(text1, text2, 2);
Zwei Strings vergleichen mit strcmp (stringcompare):
int strcmp (const char* str1, const char* str2);
Funktion: Vergleicht den String str1 mit str2
char text1[] = "abcde";
char text2[] = "abcdf";
if (strcmp(text1, text2) == 0) Serial.println("Strings sind identisch");
Rückgabe:
- 0 ... wenn die Strings identisch sind
- >0 ... wenn das erste ungleiche Zeichen in text1 ist größer als in text2
- <0 ... wenn das erste ungleiche Zeichen in text1 ist kleiner als in text2
Bestimmte Anzahl von Zeichen vergleichen mit strncmp (string-n-compare)
int strncmp (const char* str1, const char* str2, size_t n);
Funktion: Vergleicht den String str1 mit den ersten n-Zeichen von String str2
char text1[] = "abcde";
char text2[] = "abcdf";
int anzahl = 3;
if (strncmp(text1, text2, anzahl) == 0) Serial.println("die ersten 3 Zeichen sind identisch");
Rückgabe:
siehe strcmp
Strings kopieren mit strcpy (stringcopy):
char* strcpy (char* destination, const char* source);
Funktion: Kopiert source nach destination
char text1[5];
strcpy(text1, "ABCD");
oder
char text1[5];
char text2[5] = "EFGH";
strcpy(text1, text2);
n-Zeichen von Strings kopieren mit strncpy (string-n-copy)
char* strncpy (char* destination, const char* source, size_t n);
Funktion: Kopiert die ersten n-Zeichen von source nach destination
char text1[10] = "12345678";
char text2[10];
strncpy(text2, text1, 3); //3 Zeichen von text1 in text2 kopieren
text2[3] = '\0' //Ende-Zeichen setzen!!!
Länge ein Strings mit strlen (stringlength) ermitteln:
size_t strlen(const char * str);
Funktion: Ermittlung der Länge des Strings str
char string[100] = "ABCD";
int stringlaenge = strlen(string);
Ergebnis = 4
Bemerkung: Der einen String abschließenden Null-Terminator wird dabei nicht mitgezählt!!
Teilstring in String suchen mit strstr (stringstring):
const char* strstr (const char* str1, const char* str2);
char* strstr (char* str1, const char* str2);
Funktion: Vergleicht, ob String str2 in String str1 enthalten ist
char text[] = "ABCDEFG";
if (strstr(text, "CDE")) Serial.println("Teilstring gefunden");
Mit strtok (stringtoken) einen String zerteilen:
char* strtok (char* str, const char* delimiters);
Funktion: Zerteilt den String str in Teilstrings, begrenzt durch delimiters
char text[] = "Kurt,Kanns;5556;DE";
char delimiter[] = ",;"; //Teilsting-Begrenzer
char* ptr; //NULL-Pointer
ptr = strtok(text, delimiter); //Initialisieren u. erster Teilstring
while(ptr != NULL) //Weitere Teilstrings
{
Serial.println(ptr);
ptr = strtok(NULL, delimiter);
}
Ausgabe am Seriellen Monitor:
Kurt
Kanns
5556
DE
oder
char text[] = "Kurt,Kanns;5556;DE";
char* ptr = NULL; //NULL-Pointer
char vorname[10];
char nachname[20];
int personalnummer;
ptr = strtok(text, ","); //Initialisieren u. erster Teilstring
strcpy(vorname, ptr);
ptr = strtok(NULL, ";"); //Zweiter Teilstring
strcpy(nachname, ptr);
personalnummer = atoi(strtok(NULL, ";")); //Dritter Teilstring
Serial.println(Vorname);
Serial.println(Nachname);
Serial.println(Personalnummer);
Ausgabe am Seriellen Monitor:
Kurt
Kanns
5556
String löschen:
char text[10] = "abcdefg";
text[0] = '\0'
oder
strcpy(text, "");
Funktionen zur Array-Bearbeitung
(Vergleichen, kopiern und setzen)
- memcmp
- memcpy
- memset
Arrays vergleichen mit memcmp (memory compare)
int memcmp(const void* arr1, const void* arr2, size_t n);
Funktion: Vergleicht Array arr1 mit n-Zeichen von arr2
byte array1[] = {0xA1, 0xB2, 0xC9};
byte array2[] = {0xA1, 0xB2, 0xC9, 0xD1};
if (memcmp(array1, array2, sizeof(array1)) == 0) Serial.println("Arrays stimmen überein");
else Serial.println("Arrays stimmen nicht überein");
Ergebnis: Arrays stimmen überein
if (memcmp(array1, array2, sizeof(array2)) == 0) Serial.println("Arrays stimmen überein");
else Serial.println("Arrays stimmen nicht überein");
Ergebnis: Arrays stimmen nicht überein
Arrays kopieren mit memcpy (memory copy)
void* memcpy (void * destination, const void* source, size_t n );
Funktion: Kopiert n-Bytes von source nach destination
byte array1[4];
byte array2[] = {0xA1, 0xB2, 0xC9, 0xD1};
memcpy(array1, array2, sizeof(array2));
Arrays setzen mit memset (memory set)
void* memset (void* arr, int value, size_t n);
Funktion: Ersetzt n-Zeichen des Arrays arr mit value
byte array1[] = {0xA1, 0xB2, 0xC9};
byte val = 1;
memset(array1, val, 2);
Funktionen zur Typenumwandlung
- atof
- atoi
- atol
- dtostrf und ftoa
- itoa
- ltoa
String in Float umwandeln mit atof (array to float)
double atof (const char* str);
Funktion: Wandelt einen String str in eine Float-Zahl um
char zahlenstring1[] = "12.345"
float num = atof(zahlenstring1);
String in Integer umwandeln mit atoi (array to integer)
int atoi (const char* str);
Funktion: Wandelt einen String str in eine Integer-Zahl um
char zahlenstring1[] = "1234";
int num = atoi(zahlenstring1);
String in Long Integer umwandeln mit atol (array to long)
long atol (const char* str);
Funktion: Wandelt einen String str in eine Long-Integer-Zahl um
char zahlenstring1[] = "12345";
long num = atol(zahlenstring1);
Float in character Array umwandeln mit dtostrf (double to string float)
char* dtostrf(double zahl, char len, char dec, char *str)
Funktion: Wandelt eine Float-Zahl "zahl" in einen String (Character-Array) "str" mit der Länge "len" und der Anzahl von Dezimalstellen "dec" um.
char str[10];
float zahl = 23.2345;dtostrf(zahl, 1, 3, str);
Bemerkung:
len: Anzahl der Gesamtlänge des umgewandelten Strings, einschließlich Dezimalpunkt, Vor- und Nachkommastellen.
Sollte der umgewandelte String kürzer als "len" sein, dann wird links mit Leerzeichen aufgefüllt.
Sollte "len" kleiner als die notwendige Stringlänge sein, so wird der String trotzdem in seiner vollen Länge erzeugt.
Float in character Array umwandeln mit ftoa (float to array)
Um eine Float-Zahlen in ein Character-Array umzuwandeln, verwende ich auch meine eigene Funktion ftoa(), die zwar geringfügig mehr SRAM-Speicher aber weniger Flash-Speicher benötigt, was mir besonders bei Verwendung eines Attiny45/84 schon geholfen hat. Eine Beschreibung meiner Funktion ftoa() ist hier zu finden: Float to Array
Integer in String umwandeln mit itoa (integer to array)
char* itoa (int zahl, char * str, int base);
Funktion: Wandelt eine Interger-Zahl "zahl" in einen String "str" mit der Basis "base" um.
(Basis: Numerische Basis, die verwendet wird, um den Wert als eine Zeichenfolge zwischen 2 und 36 darzustellen, wobei 10 Dezimalbasis, 16 Hexadezimalwert, 8 Oktalwert und 2 Binärwert bedeutet.)
int zahl = 1245;
char str[17];
itoa(zahl, str, 10);
Long in String umwandeln mit ltoa (long to array)
char* ltoa (long zahl, char * str, int base);
Funktion: Wandelt eine Long-Interger-Zahl "zahl" in einen String "str" mit der Basis "base" um.
(Basis: Numerische Basis, die verwendet wird, um den Wert als eine Zeichenfolge zwischen 2 und 36 darzustellen, wobei 10 Dezimalbasis, 16 Hexadezimalwert, 8 Oktalwert und 2 Binärwert bedeutet.)
long zahl = 1245344;
char str[17];
ltoa(zahl, str, 10);
Sonstige Funktionalitäten
- Bedingungsoperator
- Bereichsbasierte for-Schleife
Bedingungsoperator "bedingung ? ausdruck1 : ausdruck2":
int x;
int y = 100;
int z = 1000;
x = (y > z) ? 0 : 1;
Ergebnis: x = 1
entspricht:
if (y > z) x = 0;
else x = 1;
Bereichsbasierte for-Schleife (Range-based-for-loop)
Die "Bereichsbasierte for-Schleife" ist eine alternative zur üblichen for-Schleife, bei der die Elemente eines Arrays durchlaufen werden.
Duchlaufen eines Arrays:
int x[5] = {1, 2, 3, 4, 5};
for (int y : x) Serial.print(y);
Ausgabe: 12345
Bemerkung:
- Die Schleifenvariable y ist kein Index, sondern bereits das gewünschte Array-Element. D.h. im ersten Schleifendurchgang steht y für das erste Element im Array (mit Index 0) und so weiter.
- Es werden immer alle Elemente des Arrays durchlaufen, daher müssen keine Indexgrenzen angegeben werden. Allerdings können dafür keine Teilbereiche eines Arrays durchlaufen werden.
Mit Schlüsselwort "auto":
"auto" bewirkt, dass Typinferenz verwendet wird.
(Typinferenz bedeutet Typrückschluss. In der Informatik versteht man unter Typinferenz das Folgern eines bestimmten Typs für einen gegebenen Ausdruck, obwohl der Typ nicht explizit angegeben wurde. Stattdessen wird unter Zuhilfenahme eines Algorithmus auf den richtigen Typen rückgeschlossen.)
int x[5] = {1, 2, 3, 4, 5};
for (auto y : x) Serial.print(y);
Ausgabe: 12345
Mit Typinferenz "by reference":
int x[5] = {1, 2, 3, 4, 5};
for (auto& y : x) y++;
Durch die Variablenübergabe "by referenz" wirken sich Änderungen der Schleifenvariable y auf das Array x aus. Im Beispiel ist nach dem Durchlauf der for-Schleife der Inhalt des Array x[5] = {2, 3, 4, 5, 6}.
Quellen "Bereichsbasierte for-Schleife":
Dirk Louis, 2018, C++, Hanser Verlag;
https://docs.microsoft.com/de-de/cpp/cpp/range-based-for-statement-cpp?view=vs-2019
https://intern.fh-wedel.de/~si/seminare/ws04/Ausarbeitung/4.Typcheck/staTyp4.htm