ich fange gerade an mich mit Pointern zu beschäftigen und habe hier gerade ein Verständnis Problem und hoffe, ihr könnt mir eine Antwort geben. Ich möchte ein char Array als Pointer an eine Funktion übergeben, den Inhalt verändern und nach Aufruf der Funktion erwarte ich den geänderten Inhalt an der Stelle des Pointers zurück.
Unterprogramm:
void test (char *abc) {
char *abcde = "Test1234";
Serial.println (abcde);
strcpy(abc, abcde); // Inhalt von abcde nach abc kopieren
Serial.println (abc); // Ausgabe: "Test1234"
}
Aufruf:
char *xyz; // oder char xyz[256];
xyz = (char*)"Hallo";
test (xyz);
Serial.println(xyz); // Ausgabe "Test1234" - funktioniert!
Wenn ich nun im Unterprogramm folgendes schreibe, bekomme ich nach dem Aufruf nur das "Hallo" zurück:
void test (char *abc) {
abc = (char*)"Test1234"; // Array Inhalt hier ändern
Serial.println (abc); // Ausgabe hier noch: "Test1234"
}
Aufruf:
char *xyz; // oder char xyz[256];
xyz = (char*)"Hallo";
test (xyz);
Serial.println(xyz); // Ausgabe "Hallo" - "Test1234" erwartet
Frage:
was macht "strcpy(abc, abcde);" mit dem Pointer, das durch diesen Aufruf das Array "Test1234" wieder in xyz im Hapuptprogamm auftaucht? Wird strcpy nicht aufgerufen, wird es zwar im Unterprogramm lokal geändert, nach Rücksprung zum Hauptprogramm ist in xyz wieder der alte Inhalt vorhanden.
Viele Grüße und ein glückliches und zufriedenes Jahr 2025,
Das Problem liegt darin, dass im zweiten Beispiel, wenn Sie abc = (char*)"Test1234"; zuweisen, Sie nur den Zeiger abc lokal in der Funktion test() ändern, was den Wert des Zeigers xyz in der Hauptfunktion nicht verändert. Im Gegensatz dazu kopieren Sie mit strcpy(abc, abcde); den Inhalt der Zeichenkette abcde in den Speicherbereich, auf den abc zeigt, was den Inhalt des Speichers, auf den xyz zeigt, ändert und diese Änderung nach der Rückkehr aus der Funktion bestehen bleibt.
➜ Die Zuweisung eines Zeigers (abc = (char*)"Test1234";) ändert nur die lokale Kopie des Zeigers.
➜ strcpy() ändert den Inhalt des Speichers, auf den der Zeiger zeigt, was eine bleibende Wirkung im Hauptprogramm hat.
vielen Dank für die schnelle Antwort - das habe ich verstanden! Gibt es da eine Möglichkeit, ohne "strcpy" den Inhalt des Arrays zu ändern? Man müsste doch nur irgendwie ausdrücken, das keine Lokaler Pointer erzeugt wird.
Es gibt andere Funktionen zum Füllen des Speichers (wie memcpy, ...), aber in jedem Fall läuft es darauf hinaus, eine Schleife zu machen, die die Bytes von einer Seite nimmt, um sie auf die andere Seite zu kopieren.
Dann kann ich das strcpy lassen. Ist vielleicht auch besser erst mal das Array lokal aufzuarbeiten und am Schluss die Kopie des ganzen wieder zu übernehmen.
In function 'test.constprop',
inlined from 'setup' at 18:8,
inlined from 'main' at C:\Program Files (x86)\Arduino-1.8.19\hardware\arduino\avr\cores\arduino\main.cpp:43:7:
4:9: warning: 'strcpy' writing 9 bytes into a region of size 6 overflows the destination [-Wstringop-overflow=]
strcpy(abc, abcde); // Inhalt von abcde nach abc kopieren
^
Der Sketch verwendet 1614 Bytes (5%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 220 Bytes (10%) des dynamischen Speichers, 1828 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
Diese Warnung darfst Du nicht ignorieren, da nachfolgender Speicher überschrieben wird!
Tut nicht, was Du willst, erzeugt aber dank strncpy auch keinen Laufzeitfehler:
Pointer sind böse.
Und wenn nötig, sind sie immer noch ein Übel.
So fängt es an!
E:\Programme\arduino\portable\sketchbook\sketch_dec29e\sketch_dec29e.ino: In function 'void test(char*&)':
E:\Programme\arduino\portable\sketchbook\sketch_dec29e\sketch_dec29e.ino:7:17: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
7 | char *abcde = "Test1234";
| ^~~~~~~~~~
Und so endet es dann im "undefined behavier":
In function 'test',
inlined from 'setup' at E:\Programme\arduino\portable\sketchbook\sketch_dec29e\sketch_dec29e.ino:23:9,
inlined from 'main' at E:\Programme\arduino\portable\packages\arduino\hardware\avr\1.8.6\cores\arduino\main.cpp:43:7:
E:\Programme\arduino\portable\sketchbook\sketch_dec29e\sketch_dec29e.ino:9:9: warning: 'strcpy' writing 9 bytes into a region of size 6 overflows the destination [-Wstringop-overflow=]
9 | strcpy(abc, abcde); // Inhalt von abcde nach abc kopieren
| ^
Der Tip mit strncpy ist natürlich auch gut! Ich hätte hier noch vor dem strcpy eine Abfrage eingebaut, damit das nicht passiert. Aber so ist es natürlich perfekt!
Mir ging es auch in erster Linie nur darum, warum das mit strcpy geht und andersrum nicht.
Den Rest... Naja da kann ich mich jetzt nicht raus reden. War halt schnell dahin getippt und wenn man einen Fehler hat, probiert man halt auch mal das eine oder andere aus und vergisst dann auch mal ein (*char). Aber trotzden gut, das darauf hingewiesen wurde, wenn jemand den Thread mal öffnet, weiß er wenigstens wie es richtig geht
Deine freundlich dreinblickenden japanischen Masken lassen mich das Problem nicht erkennen. strlen zählt die Zeichen ohne Endezeichen '\0', weshalb das vorhandene Endezeichen im Ziel bei strncpy nicht überschrieben werden kann. Daher dürfte nach meinem Verständnis der zitierte Satz auch keine Auswirkung haben.
Oder?
Bei strlen(xyz) + 1 geht es schief, was nicht verwundert.
Programm
#include <Streaming.h> // https://github.com/janelia-arduino/Streaming
void test (char *abc, const size_t len)
{
char *abcde = (char*)"Test1234";
Serial << "abcde: " << abcde << endl;
strncpy(abc, abcde, len); // Inhalt von abcde nach abc kopieren
Serial << " abc: " << abc << endl; // Ausgabe: "Test1234"
}
void setup()
{
Serial.begin(9600);
Serial << "\nStart\n";
char *xyz = (char*)"Hallo";
Serial << "1 xyz: " << xyz << endl;
test (xyz, strlen(xyz));
Serial << "2 xyz: " << xyz << endl;
test (xyz, strlen(xyz) + 1);
Serial << "3 xyz: " << xyz << endl;
}
void loop() {}
Das Problem wohnt eine Schicht tiefer.
Redundanzen!
Und diese öffnen die Tür für copy&paste, Tippfehler und sonstige kleine Irrtümer.
Dass solche Irrtümer passieren können sieht man hier:
warning: 'strcpy' writing 9 bytes into a region of size 6 overflows the destination
Das führt zum Abschmierer des Programms, wg. UB
Das (hier) wird zum Glück bemerkt und gewarnt.
Aber das ist längst nicht immer so.
Dann sind solche Fehler recht schwer zu finden.
In C gab es keine Alternative, als die Länge explizit mit zu übergeben.
In C++ haben wir mehr Möglichkeiten, dieser Falle aus dem Wege zu gehen.
Auch das hier halte ich für gefährlich:
Unangenehme Seiteneffekte drohen.
Siehe:
#include <Streaming.h> // https://github.com/janelia-arduino/Streaming
void test (char *abc, const size_t len)
{
char *abcde = (char*)"Test1234";
Serial << "abcde: " << abcde << endl;
strncpy(abc, abcde, len); // Inhalt von abcde nach abc kopieren
Serial << " abc: " << abc << endl; // Ausgabe: "Test1234"
}
void setup()
{
Serial.begin(9600);
Serial << "\nStart\n";
char *xyz = (char*)"Hallo";
Serial << "1 xyz: " << xyz << endl;
test (xyz, strlen(xyz));
Serial << "2 xyz: " << xyz << endl;
test (xyz, strlen(xyz) + 1);
Serial << "3 xyz: " << xyz << endl;
Serial << "4 Hallo: " << "Hallo" << endl;
}
void loop() {}
Das schmeckt mir alles nicht wirklich! (das Pointergehampel)
Mag sein, dass dir keine bis wenig Irrtümer passieren.
Mir allerdings schon einige.
Da hinterher suchen zu wollen, fehlt mir der Antrieb. Da mache ich es doch lieber im Vorfeld, mit etwas Disziplin, richtig
Wenn Du von Redundanz schreibst, wird "verlorener Text" eventuell nur einmal mit mehreren Zeigern gespeichert. Wird der Text verändert, sieht auch der zweite Zeiger den veränderten Inhalt. Nach dem Fehler möchte ich nicht suchen müssen!
Bei mir gekennzeichnet mit Bitte nicht nachmachen!
Danke! Dir wünsche ich fleißige und gesunde Bienen in 2025!
@tubie1977: Diese Verwendung der Zeiger solltest Du nochmal überdenken!
Erster, Parameter ist ein Pointer
Zweiter, die Länge
Leider geht beim Umwandeln von einem Char Array in einen Pointer die wahre Array Länge/Größe verloren.
Wenn irgend die Chance besteht, sollte man das Array übergeben, denn dessen Datentype enthält die Array Länge/Größe. Zumindest kann man sie aus dem Datentype ermitteln. Tut man das, gibt es eine Redundanz weniger. Eine potentielle Fehlerquelle weniger.
Ja, eine dokumentierte Optimierung.
#include <Streaming.h> // https://github.com/janelia-arduino/Streaming
void setup()
{
Serial.begin(9600);
Serial << "\nStart\n";
char *a = (char*)"Der braune Frosch";
char *b = (char*)"Frosch";
char c[] = "Der braune Frosch";
Serial << "1a: " << a << endl;
Serial << "1b: " << b << endl;
Serial << "1c: " << c << endl;
b[0] = 'X';
Serial << "2a: " << a << endl;
Serial << "2b: " << b << endl;
Serial << "2c: " << c << endl;
}
void loop() {}
Ausgabe:
Start
1a: Der braune Frosch
1b: Frosch
1c: Der braune Frosch
2a: Der braune Xrosch
2b: Xrosch
2c: Der braune Frosch
Mein Rat:
Zeiger, wenn es geht, verzichten.
C Type Casts, ebenso.